Skip to content

Commit

Permalink
Merge pull request Leaflet#672 from DatapuntAmsterdam/polygon-distance
Browse files Browse the repository at this point in the history
Polygon line length, square kilometers and configurable precision
  • Loading branch information
ddproxy authored Jun 12, 2017
2 parents 95de4b0 + 4608488 commit 5b58774
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 142 deletions.
10 changes: 9 additions & 1 deletion build/docs-misc.leafdoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ for this section of the documentation, please edit docs-misc.leafdoc in the buil
| drawError | Object | [See code](https://github.com/Leaflet/Leaflet.draw/blob/master/src/draw/handler/Draw.Polyline.js#L10) | Configuration options for the error that displays if an intersection is detected.
| guidelineDistance | Number | `20` | Distance in pixels between each guide dash.
| shapeOptions | [Leaflet Polyline options](http://leafletjs.com/reference.html#polyline-options) | [See code](https://github.com/Leaflet/Leaflet.draw/blob/master/src/draw/handler/Draw.Polyline.js#L20) | The options used when drawing the polyline/polygon on the map.
| metric | Bool | `true` | Determines which measurement system (metric or imperial) is used.
| showLength | Bool | `true` | Show the length of the drawn line. **The area is only approximate and become less accurate the larger the polygon is.**
| metric | Bool | `true` | Use the metric measurement system.
| feet | Bool | `true` | Use feet instead of yards and miles, when not using the metric measurement system.
| nautic | Bool | `false` | Use nautic miles instead of yards and miles, when not using the metric measurement system nor feet.
| zIndexOffset | Number | `2000` | This should be a high number to ensure that you can draw over all other layers on the map.
| repeatMode | Bool | `false` | Determines if the draw tool remains enabled after drawing a shape.

Expand All @@ -64,6 +67,11 @@ for this section of the documentation, please edit docs-misc.leafdoc in the buil
| Option | Type | Default | Description
| --- | --- | --- | ---
| showArea | Bool | `false` | Show the area of the drawn polygon in m², ha or km². **The area is only approximate and become less accurate the larger the polygon is.**
| showLength | Bool | `false` | Show the length of the drawn line. **The area is only approximate and become less accurate the larger the polygon is.**
| metric | Object | `true` | Use the metric measurement system. Can be a boolean value, but can also be an array to specify which units to use. Possible units are `km` (kilometers), `ha` (hectares), `m` (metres). So a value of `['km', 'm']` means that the length will be shown in metres and, when more than a 1000 metres, in kilometers, and the area will be shown in m² or km² and acres will not be used.
| feet | Bool | `true` | Use feet instead of yards and miles, when not using the metric measurement system.
| nautic | Bool | `false` | Use nautic miles instead of yards and miles, when not using the metric measurement system nor feet.
| precision | Object | `{km: 2, ha: 2, m: 0, mi: 2, ac: 2, yd: 0, ft: 0, nm: 2}` | Defines the precision to use for numbers of each type of unit. Possible units are `km` (kilometers), `ha` (hectares), `m` (metres), `mi` (miles), `ac` (acres), `ya` (yards), `ft` (feet), `nm` (nautical miles). For example `{km: 1}` changes the default precision for km and km² to one which gives values like `1.5 km` and `15.0 km²` in stead of `1.53 km` and `15.01 km²`.


@namespace RectangleOptions
Expand Down
255 changes: 169 additions & 86 deletions spec/suites/GeometryUtilSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,104 +10,187 @@ describe("L.GeometryUtil", function () {
});

describe("readableDistance", function () {
it("metric", function () {
expect(L.GeometryUtil.readableDistance(1000, true)).to.eql('1000 m');
expect(L.GeometryUtil.readableDistance(1500, true)).to.eql('1.50 km');
expect(L.GeometryUtil.readableDistance(1500, 'metric')).to.eql('1.50 km');
describe("metric", function () {
it("returns meters or kilometers", function() {
expect(L.GeometryUtil.readableDistance(1000, true)).to.eql('1000 m');
expect(L.GeometryUtil.readableDistance(1500, true)).to.eql('1.50 km');
});

it("is used when 'metric' is specified", function() {
expect(L.GeometryUtil.readableDistance(1500, 'metric')).to.eql('1.50 km');
});

it("is used even when other flags are set", function() {
expect(L.GeometryUtil.readableDistance(1500, true, true, true)).to.eql('1.50 km');
});

it("switches from meters to kilometers on more than 1000 meters", function() {
expect(L.GeometryUtil.readableDistance(999, true)).to.eql('999 m');
expect(L.GeometryUtil.readableDistance(1000, true)).to.eql('1000 m');
expect(L.GeometryUtil.readableDistance(1001, true)).to.eql('1.00 km');
expect(L.GeometryUtil.readableDistance(1002, true)).to.eql('1.00 km');
});

it("uses the precision specified", function () {
var precision = {
km: 0,
m: 2
};

expect(L.GeometryUtil.readableDistance(1000, true, false, false, precision)).to.eql('1000.00 m');
expect(L.GeometryUtil.readableDistance(100.123, true, false, false, precision)).to.eql('100.12 m');
expect(L.GeometryUtil.readableDistance(100.456, true, false, false, precision)).to.eql('100.46 m');
expect(L.GeometryUtil.readableDistance(1001, true, false, false, precision)).to.eql('1 km');
expect(L.GeometryUtil.readableDistance(1500, true, false, false, precision)).to.eql('2 km');
expect(L.GeometryUtil.readableDistance(2000, true, false, false, precision)).to.eql('2 km');
});
});

it("imperial", function () {
expect(L.GeometryUtil.readableDistance(1609.3488537961)).to.eql('1760 yd');
expect(L.GeometryUtil.readableDistance(1610.3488537961)).to.eql('1.00 miles');
expect(L.GeometryUtil.readableDistance(1610.3488537961, 'yards')).to.eql('1.00 miles');
describe("imperial", function () {
it("returns yards or miles", function() {
expect(L.GeometryUtil.readableDistance(1609.3488537961)).to.eql('1760 yd');
expect(L.GeometryUtil.readableDistance(1610.3488537961)).to.eql('1.00 miles');
});

it("is used when 'yards' is specified", function() {
expect(L.GeometryUtil.readableDistance(1610.3488537961, 'yards')).to.eql('1.00 miles');
});

it("switches from yards to miles on more than 1760 yards", function() {
expect(L.GeometryUtil.readableDistance(1608.3488537961)).to.eql('1759 yd');
expect(L.GeometryUtil.readableDistance(1609.3488537961)).to.eql('1760 yd');
expect(L.GeometryUtil.readableDistance(1610.3488537961)).to.eql('1.00 miles');
expect(L.GeometryUtil.readableDistance(1611.3488537961)).to.eql('1.00 miles');
});

it("uses the precision specified", function () {
var precision = {
mi: 0,
yd: 2
};

expect(L.GeometryUtil.readableDistance(1609.3488537961, false, false, false, precision)).to.eql('1760.00 yd');
expect(L.GeometryUtil.readableDistance(1609.2488537961, false, false, false, precision)).to.eql('1759.89 yd');
expect(L.GeometryUtil.readableDistance(1610.3488537961, false, false, false, precision)).to.eql('1 miles');
expect(L.GeometryUtil.readableDistance(2415.3488537961, false, false, false, precision)).to.eql('2 miles');
expect(L.GeometryUtil.readableDistance(3218.3488537961, false, false, false, precision)).to.eql('2 miles');
});
});

it("imperial feet", function () {
expect(L.GeometryUtil.readableDistance(1609.3488537961, false, true, false)).to.eql('5280 ft');
expect(L.GeometryUtil.readableDistance(1610.3488537961, false, true, false)).to.eql('5284 ft');
expect(L.GeometryUtil.readableDistance(1610.3488537961, 'feet')).to.eql('5284 ft');
describe("imperial feet", function () {
it("always returns feet", function() {
expect(L.GeometryUtil.readableDistance(1609.3488537961, false, true, false)).to.eql('5280 ft');
expect(L.GeometryUtil.readableDistance(1610.3488537961, false, true, false)).to.eql('5283 ft');
});

it("is used when 'feet' is specified", function() {
expect(L.GeometryUtil.readableDistance(1610.3488537961, 'feet')).to.eql('5283 ft');
});

it("uses the precision specified", function () {
var precision = {
ft: 2
};

expect(L.GeometryUtil.readableDistance(1609.3488537961, false, true, false, precision)).to.eql('5280.00 ft');
expect(L.GeometryUtil.readableDistance(1609.4488537961, false, true, false, precision)).to.eql('5280.33 ft');
});
});

it("nautical", function () {
expect(L.GeometryUtil.readableDistance(1609.3488537961, false, false, true)).to.eql('0.87 nm');
expect(L.GeometryUtil.readableDistance(1610.3488537961, false, false, true)).to.eql('0.87 nm');
expect(L.GeometryUtil.readableDistance(1610.3488537961, 'nauticalMile')).to.eql('0.87 nm');
describe("nautical", function () {
it("always returns nautical miles", function() {
expect(L.GeometryUtil.readableDistance(1609.3488537961, false, false, true)).to.eql('0.87 nm');
expect(L.GeometryUtil.readableDistance(1610.3488537961, false, false, true)).to.eql('0.87 nm');
});

it("is used when 'nauticalMile' is specified", function() {
expect(L.GeometryUtil.readableDistance(1610.3488537961, 'nauticalMile')).to.eql('0.87 nm');
});

it("uses the precision specified", function () {
var precision = {
nm: 3
};

expect(L.GeometryUtil.readableDistance(1609.3488537961, false, false, true, precision)).to.eql('0.869 nm');
expect(L.GeometryUtil.readableDistance(1610.3488537961, false, false, true, precision)).to.eql('0.870 nm');
});
});
});

describe("formatted number", function () {
it("accepts a thousands seperator", function () {
L.drawLocal.format = {
numeric: {
delimiters: {
thousands: '#',
}
}
};
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100.00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1#000.00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1#000#000.00');
L.drawLocal.format = {
numeric: {
delimiters: {
thousands: '#',
}
}
};
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100.00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1#000.00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1#000#000.00');
});

it("accepts a decimal seperator", function () {
L.drawLocal.format = {
numeric: {
delimiters: {
decimal: '$'
}
}
};
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100$00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1000$00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1000000$00');
});

it("accepts a thousands and a decimal seperator", function () {
L.drawLocal.format = {
numeric: {
delimiters: {
thousands: '#',
decimal: '$'
}
}
};
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100$00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1#000$00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1#000#000$00');
});

it("defaults to no thousands and decimal dot", function () {
delete L.drawLocal.format;
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100.00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1000.00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1000000.00');
});

it("accepts a decimal seperator", function () {
L.drawLocal.format = {
numeric: {
delimiters: {
decimal: '$'
}
}
};
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100$00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1000$00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1000000$00');
});

it("accepts a thousands and a decimal seperator", function () {
L.drawLocal.format = {
numeric: {
delimiters: {
thousands: '#',
decimal: '$'
}
}
};
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1#000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1#000#000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100$00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1#000$00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1#000#000$00');
});

it("defaults to no thousands and decimal dot", function () {
delete L.drawLocal.format;
expect(L.GeometryUtil.formattedNumber(100)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 0)).to.eql('100');
expect(L.GeometryUtil.formattedNumber(1000, 0)).to.eql('1000');
expect(L.GeometryUtil.formattedNumber(1000000, 0)).to.eql('1000000');
expect(L.GeometryUtil.formattedNumber(100, 2)).to.eql('100.00');
expect(L.GeometryUtil.formattedNumber(1000, 2)).to.eql('1000.00');
expect(L.GeometryUtil.formattedNumber(1000000, 2)).to.eql('1000000.00');
});

it("is used for readableDistance and readableArea", function () {
it("is used for readableDistance and readableArea", function () {
L.drawLocal.format = {
numeric: {
delimiters: {
Expand All @@ -117,7 +200,7 @@ describe("L.GeometryUtil", function () {
}
};
expect(L.GeometryUtil.readableDistance(1000, true)).to.eql('1.000 m');
expect(L.GeometryUtil.readableArea(50000, true)).to.eql('5,00 ha');
expect(L.GeometryUtil.readableArea(50000, true)).to.eql('5,00 ha');
});
});
});
13 changes: 12 additions & 1 deletion src/Tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ L.Draw.Tooltip = L.Class.extend({
initialize: function (map) {
this._map = map;
this._popupPane = map._panes.popupPane;
this._visible = false;

this._container = map.options.drawControlTooltips ?
L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;
Expand Down Expand Up @@ -64,6 +65,14 @@ L.Draw.Tooltip = L.Class.extend({
'<span class="leaflet-draw-tooltip-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
'<span>' + labelText.text + '</span>';

if (!labelText.text && !labelText.subtext) {
this._visible = false;
this._container.style.visibility = 'hidden';
} else {
this._visible = true;
this._container.style.visibility = 'inherit';
}

return this;
},

Expand All @@ -74,7 +83,9 @@ L.Draw.Tooltip = L.Class.extend({
tooltipContainer = this._container;

if (this._container) {
tooltipContainer.style.visibility = 'inherit';
if (this._visible) {
tooltipContainer.style.visibility = 'inherit';
}
L.DomUtil.setPosition(tooltipContainer, pos);
}

Expand Down
27 changes: 23 additions & 4 deletions src/draw/handler/Draw.Polygon.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ L.Draw.Polygon = L.Draw.Polyline.extend({

options: {
showArea: false,
showLength: false,
shapeOptions: {
stroke: true,
color: '#3388ff',
Expand All @@ -22,7 +23,14 @@ L.Draw.Polygon = L.Draw.Polyline.extend({
fillOpacity: 0.2,
clickable: true
},
metric: true // Whether to use the metric measurement system or imperial
// Whether to use the metric measurement system (truthy) or not (falsy).
// Also defines the units to use for the metric system as an array of
// strings (e.g. `['ha', 'm']`).
metric: true,
feet: true, // When not metric, to use feet instead of yards for display.
nautic: false, // When not metric, not feet use nautic mile for display
// Defines the precision for each type of unit (e.g. {km: 2, ft: 0}
precision: {}
},

// @method initialize(): void
Expand Down Expand Up @@ -58,6 +66,7 @@ L.Draw.Polygon = L.Draw.Polyline.extend({
text = L.drawLocal.draw.handlers.polygon.tooltip.start;
} else if (this._markers.length < 3) {
text = L.drawLocal.draw.handlers.polygon.tooltip.cont;
subtext = this._getMeasurementString();
} else {
text = L.drawLocal.draw.handlers.polygon.tooltip.end;
subtext = this._getMeasurementString();
Expand All @@ -70,13 +79,23 @@ L.Draw.Polygon = L.Draw.Polyline.extend({
},

_getMeasurementString: function () {
var area = this._area;
var area = this._area,
measurementString = '';


if (!area) {
if (!area && !this.options.showLength) {
return null;
}

return L.GeometryUtil.readableArea(area, this.options.metric);
if (this.options.showLength) {
measurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this);
}

if (area) {
measurementString += '<br>' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision);
}

return measurementString;
},

_shapeIsValid: function () {
Expand Down
Loading

0 comments on commit 5b58774

Please sign in to comment.