Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions dist/L.Control.Heightgraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -5082,8 +5082,8 @@ var schemeSet3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9b
* calculates minimum and maximum values for the elevation scale drawn with d3
*/
_calculateElevationBounds: function _calculateElevationBounds() {
var max = d3Max(this._elevations);
var min = d3Min(this._elevations);
var max = d3Max(this._elevations) || 10;
var min = d3Min(this._elevations) || 0;
var range = max - min;
this._elevationBounds = {
min: range < 10 ? min - 10 : min - 0.1 * range,
Expand All @@ -5094,7 +5094,7 @@ var schemeSet3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9b
/**
* Creates a marker on the map while hovering
* @param {Object} ll: actual coordinates of the route
* @param {Number} height: height as float
* @param {*} height: height as float or undefined text
* @param {string} type: type of element
*/
_showMapMarker: function _showMapMarker(ll, height, type) {
Expand Down Expand Up @@ -5358,6 +5358,17 @@ var schemeSet3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9b
this._svg.append('g').attr("transform", "translate(-2,0)").attr('class', 'y axis').call(this._yAxis);
},

/**
* Returns if the given data element is defined, in order to handle missing
* elevation values and show them as gap. Implements a d3 defined accessor
* that can be passed to area/line.defined.
* @param {*} d data element
* @return {boolean} true, if elevation value is defined, false otherwise
*/
_defined: function _defined(d) {
return d && d.altitude !== undefined && d.altitude !== null;
},

/**
* Appends the areas to the graph
*/
Expand All @@ -5371,7 +5382,7 @@ var schemeSet3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9b
return xDiagonalCoordinate;
}).y0(this._svgHeight).y1(function (d) {
return self._y(d.altitude);
}).curve(curveLinear);
}).curve(curveLinear).defined(this._defined);
this._areapath = this._svg.append("path").attr("class", "area");

this._areapath.datum(block).attr("d", this._area).attr("stroke", c).styles(this._graphStyle).style("fill", c).style("pointer-events", "none");
Expand Down Expand Up @@ -5584,7 +5595,7 @@ var schemeSet3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9b
}).y(function (d) {
var y = self._y;
return y(d.altitude);
}).curve(curveBasis);
}).curve(curveBasis).defined(this._defined);

this._svg.append("svg:path").attr("d", borderTopLine(data)).attr('class', 'border-top');
},
Expand Down Expand Up @@ -5692,7 +5703,7 @@ var schemeSet3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9b
_internalMousemoveHandler: function _internalMousemoveHandler(item) {
var showMapMarker = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var areaLength;
var alt = item.altitude,
var alt = this._defined(item) ? item.altitude : '-',
dist = item.position,
ll = item.latlng,
areaIdx = item.areaIdx,
Expand Down
2 changes: 1 addition & 1 deletion dist/L.Control.Heightgraph.min.js

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions example/example-missing-elevation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
(function () {
function createLineString(altitudes, mask, index) {
let coords = [];
let lon = index || 0;
for (let i = 0; i < altitudes.length; i++) {
const ele = mask[i] ? eleBase + altitudes[i] * eleFactor : undefined;
coords.push([lon++, 0, ele]);
}

return {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": coords,
},
"properties": {
"attributeType": "0"
}
};
}

function createGeoJson(altitudesList, maskList) {
let features = [];
let index = 0
for (let i = 0; i < altitudesList.length; i++) {
const altitudes = altitudesList[i];

features.push(createLineString(altitudes, maskList[i], index));
index += altitudes.length -1;
}

return [{
"type":"FeatureCollection",
"features": features,
"properties": {
"records": 1,
"summary": " "
}
}];
}

function add(map, altitudesList, ...maskListParams) {
let layer;
const layersControl = L.control.layers({}, {}, {
collapsed: false,
position: 'bottomleft'
}).addTo(map);

for (let i = 0; i < maskListParams.length; i++) {
const maskList = maskListParams[i];

const geoJson = createGeoJson(altitudesList, maskList);

const hg = L.control.heightgraph({
position: "topright",
height: 150,
width: 600,
margins: {
top: 10,
right: 30,
bottom: 30,
left: 50
},
mappings: {
' ': {
'0': {
text: 'none',
color: '#1f77b4'
}
}
}
});
hg.addTo(map);
hg.addData(geoJson);
hg._svg.selectAll(".legend-hover").remove();

const onRoute = event => {
hg.mapMousemoveHandler(event, {showMapMarker:true})
}
const outRoute = event => {
hg.mapMouseoutHandler(0)
}

layer = L.geoJson(geoJson,{
weight: 10
}).on({
'mousemove': onRoute,
'mouseout': outRoute,
});

layersControl.addBaseLayer(layer, JSON.stringify(maskList));
}

layer.addTo(map);
}

const map = new L.Map('map');
map.setView([0,6], 6.5);

const eleBase = 500;
const eleFactor = 10;

// show variants of missing elevation defined by masks
add(map,
[[4,5,0,4],[4,6,2,1]],
[[1,1,1,1],[1,1,1,1]],
[[1,1,1,1],[0,0,1,1]],
[[0,0,1,1],[1,1,1,1]],
[[1,1,1,1],[1,1,0,0]],
[[0,0,0,0],[0,0,0,0]]
);
}());
3 changes: 2 additions & 1 deletion example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@

</head>
<body>
<div id="overlay" style="position:absolute; bottom:10px; left:10px; background-color:white; width:100px; height:150px; z-index: 1000 ; opacity: 0.7">
<div id="overlay" style="position:absolute; bottom:10px; left:10px; background-color:white; width:100px; height:180px; z-index: 1000 ; opacity: 0.7">
<button onclick="changeData('1')">Data 1 <br>(4 cat)</button>
<button onclick="changeData('2')">Data 2 <br>(1 cat)</button>
<button onclick="changeData('3')">Data 3 <br>(1 different)</button>
<button onclick="changeData('4')">Data 4 <br>(empty)</button>
<a href="../example/missing-elevation.html">Missing <br>elevation</a>
</div>
<div id="map"></div>
</body>
Expand Down
24 changes: 24 additions & 0 deletions example/missing-elevation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Height Graph Missing Elevation Example</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style>
html, body, #map {
height:100%;
width:100%;
padding:0;
margin:0;
}
</style>
<link rel="stylesheet" href="../node_modules/leaflet/dist/leaflet.css" />
<script src="../node_modules/leaflet/dist/leaflet.js"></script>
<link rel="stylesheet" href="../src/L.Control.Heightgraph.css"/>
<script type="module" src="../src/L.Control.Heightgraph.js"></script>
</head>
<body>
<div id="map"></div>
</body>
<script type="text/javascript" src="../example/example-missing-elevation.js" defer></script>
</html>
77 changes: 77 additions & 0 deletions spec/L.Control.Heightgraph.Spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,80 @@ describe('L.Control.Heightgraph', () => {
});
});

describe('L.Control.Heightgraph', () => {
let hg;

function ctx(element, attributeName) {
let s = element.tagName;
if (element.classList.length > 0) {
s += '.' + element.classList.value;
}
s += '[' + attributeName + ']';
return s;
}

beforeEach(() => {
const geoJson = [
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[0, 0],
[1, 0],
[2, 0],
[3, 0]
]
},
"properties": {
"attributeType": "0"
}
},
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[3, 0],
[4, 0],
[5, 0],
[6, 0]
]
},
"properties": {
"attributeType": "0"
}
}
],
"properties": {
"records": 1
}
}
];

hg = L.control.heightgraph();
hg.onAdd();
hg.addData(geoJson);
hg._background.style("pointer-events", "none");
// uncomment to render on test page
//document.body.appendChild(hg._container);
});

it('handles missing elevation values', () => {
// setting invalid SVG element attributes does not throw, it just logs an error (Chrome) to the console,
// but `spyOn(console, 'error')` doesn't work, therefore test for invalid values in known attributes
const div = hg._container;
div.querySelectorAll('[d]').forEach(item => {
expect(item.getAttribute('d')).withContext(ctx(item, 'd')).not.toContain("NaN");
});
div.querySelectorAll('[y]').forEach(item => {
expect(item.getAttribute('y')).withContext(ctx(item, 'y')).not.toBe("NaN");
});
div.querySelectorAll('[transform]').forEach(item => {
expect(item.getAttribute('transform')).withContext(ctx(item, 'transform')).not.toContain("undefined");
});
});
});
34 changes: 25 additions & 9 deletions src/L.Control.Heightgraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ import {
* calculates minimum and maximum values for the elevation scale drawn with d3
*/
_calculateElevationBounds() {
const max = d3Max(this._elevations)
const min = d3Min(this._elevations)
const max = d3Max(this._elevations) || 10
const min = d3Min(this._elevations) || 0
const range = max - min
this._elevationBounds = {
min: range < 10 ? min - 10 : min - 0.1 * range,
Expand All @@ -456,7 +456,7 @@ import {
/**
* Creates a marker on the map while hovering
* @param {Object} ll: actual coordinates of the route
* @param {Number} height: height as float
* @param {*} height: height as float or undefined text
* @param {string} type: type of element
*/
_showMapMarker(ll, height, type) {
Expand Down Expand Up @@ -767,17 +767,32 @@ import {
.attr('class', 'y axis')
.call(this._yAxis);
},
/**
* Returns if the given data element is defined, in order to handle missing
* elevation values and show them as gap. Implements a d3 defined accessor
* that can be passed to area/line.defined.
* @param {*} d data element
* @return {boolean} true, if elevation value is defined, false otherwise
*/
_defined(d) {
return d && d.altitude !== undefined && d.altitude !== null;
},
/**
* Appends the areas to the graph
*/
_appendAreas(block, idx, eleIdx) {
const c = this._categories[idx].attributes[eleIdx].color
const self = this
const area = this._area = d3Area().x(d => {
const xDiagonalCoordinate = self._x(d.position)
d.xDiagonalCoordinate = xDiagonalCoordinate
return xDiagonalCoordinate
}).y0(this._svgHeight).y1(d => self._y(d.altitude)).curve(curveLinear)
const area = this._area = d3Area()
.x(d => {
const xDiagonalCoordinate = self._x(d.position)
d.xDiagonalCoordinate = xDiagonalCoordinate
return xDiagonalCoordinate
})
.y0(this._svgHeight)
.y1(d => self._y(d.altitude))
.curve(curveLinear)
.defined(this._defined)
this._areapath = this._svg.append("path")
.attr("class", "area");
this._areapath.datum(block)
Expand Down Expand Up @@ -1004,6 +1019,7 @@ import {
return y(d.altitude)
})
.curve(curveBasis)
.defined(this._defined)
this._svg.append("svg:path")
.attr("d", borderTopLine(data))
.attr('class', 'border-top');
Expand Down Expand Up @@ -1077,7 +1093,7 @@ import {
*/
_internalMousemoveHandler(item, showMapMarker = true) {
let areaLength
const alt = item.altitude, dist = item.position,
const alt = this._defined(item) ? item.altitude : '-', dist = item.position,
ll = item.latlng, areaIdx = item.areaIdx, type = item.type
const boxWidth = this._dynamicBoxSize(".focusbox text")[1] + 10
if (areaIdx === 0) {
Expand Down