From e0eb7cad7e2a2f5d3ad81cbd69e5e0a05d796c73 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 8 May 2024 10:48:29 -0400 Subject: [PATCH] Add units tests --- app/static/app/js/classes/Units.js | 91 ++++++++++++++----- app/static/app/js/components/Map.jsx | 16 ++++ app/static/app/js/components/UnitSelector.jsx | 10 +- .../app/js/components/tests/Units.test.jsx | 41 +++++++++ app/static/app/js/main.jsx | 8 -- 5 files changed, 130 insertions(+), 36 deletions(-) create mode 100644 app/static/app/js/components/tests/Units.test.jsx diff --git a/app/static/app/js/classes/Units.js b/app/static/app/js/classes/Units.js index c546ee39..b9fb2d9d 100644 --- a/app/static/app/js/classes/Units.js +++ b/app/static/app/js/classes/Units.js @@ -1,4 +1,4 @@ -import { _ } from '../classes/gettext'; +import { _ } from './gettext'; const units = { acres: { @@ -47,13 +47,19 @@ const units = { factor: 10.7639, label: _('Square Feet'), abbr: 'ft²', - round: 2, + round: 2 }, sqmeters: { factor: 1, label: _('Square Meters'), abbr: 'm²', - round: 2, + round: 2 + }, + sqmeters: { + factor: 0.000001, + label: _('Square Kilometers'), + abbr: 'km²', + round: 5 }, sqmiles: { factor: 0.000000386102, @@ -61,36 +67,68 @@ const units = { abbr: 'mi²', round: 5 } - }; +}; + +class ValueUnit{ + constructor(val, unit){ + this.val = val; + this.unit = unit; + } + + toString(){ + const mul = Math.pow(10, this.unit.round); + const rounded = (Math.round(this.val * mul) / mul).toString(); + + let withCommas = ""; + let parts = rounded.split("."); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + withCommas = parts.join("."); + + return `${withCommas} ${this.unit.abbr}`; + } +} class UnitSystem{ lengthUnit(meters){ throw new Error("Not implemented"); } areaUnit(sqmeters){ throw new Error("Not implemented"); } + getName(){ throw new Error("Not implemented"); } - area(meters){ - + area(sqmeters){ + const unit = this.areaUnit(sqmeters); + const val = unit.factor * sqmeters; + return new ValueUnit(val, unit); } - length(sqmeters){ - const unit = this.lengthUnit(sqmeters); - const v = unit.factor * sqmeters; - return {v, s: `{v.toLocaleString()}` }; + length(meters){ + const unit = this.lengthUnit(meters); + const val = unit.factor * meters; + return new ValueUnit(val, unit); } }; -class Metric extends UnitSystem{ +class MetricSystem extends UnitSystem{ + getName(){ + return _("Metric"); + } + lengthUnit(meters){ - if (meters < 100) return units.centimeters; + if (meters < 1) return units.centimeters; else if (meters >= 1000) return units.kilometers; else return units.meters; } areaUnit(sqmeters){ - return units.sqmeters; // TODO + if (sqmeters >= 10000 && sqmeters < 1000000) return units.hectares; + else if (sqmeters >= 1000000) return units.sqkilometers; + return units.sqmeters; } } -class Imperial extends UnitSystem{ +class ImperialSystem extends UnitSystem{ + getName(){ + return _("Imperial"); + } + lengthUnit(meters){ const feet = units.feet.factor * meters; if (feet >= 5280) return units.miles; @@ -98,7 +136,7 @@ class Imperial extends UnitSystem{ } areaUnit(sqmeters){ - const sqfeet = units.sqfeet.factor * meters; + const sqfeet = units.sqfeet.factor * sqmeters; if (sqfeet >= 43560 && sqfeet < 27878400) return units.acres; else if (sqfeet >= 27878400) return units.sqmiles; else return units.sqfeet; @@ -106,18 +144,21 @@ class Imperial extends UnitSystem{ } const systems = { - metric: new Metric(), - - - // TODO + metric: new MetricSystem(), + imperial: new ImperialSystem() } -let a = 100; -let S = systems.metric; +// Expose to allow every part of the app to access this information +function getPreferredUnitSystem(){ + return localStorage.getItem("preferred_unit_system") || "metric"; +} +function setPreferredUnitSystem(system){ + localStorage.setItem("preferred_unit_system", system); +} - -export default { - // to be used on individual strings - +export { + systems, + getPreferredUnitSystem, + setPreferredUnitSystem }; diff --git a/app/static/app/js/components/Map.jsx b/app/static/app/js/components/Map.jsx index 6b988deb..b04274fd 100644 --- a/app/static/app/js/components/Map.jsx +++ b/app/static/app/js/components/Map.jsx @@ -27,6 +27,7 @@ import '../vendor/leaflet/Leaflet.Ajax'; import 'rbush'; import '../vendor/leaflet/leaflet-markers-canvas'; import { _ } from '../classes/gettext'; +import UnitSelector from './UnitSelector'; class Map extends React.Component { static defaultProps = { @@ -404,6 +405,21 @@ class Map extends React.Component { position:'bottomleft' }).addTo(this.map); + const UnitsCtrl = Leaflet.Control.extend({ + options: { + position: 'bottomleft' + }, + + onAdd: function () { + this.container = Leaflet.DomUtil.create('div', 'leaflet-control-units-selection leaflet-control'); + Leaflet.DomEvent.disableClickPropagation(this.container); + ReactDOM.render(, this.container); + return this.container; + } + }); + new UnitsCtrl().addTo(this.map); + + if (showBackground) { this.basemaps = {}; diff --git a/app/static/app/js/components/UnitSelector.jsx b/app/static/app/js/components/UnitSelector.jsx index f754e9dd..37361de1 100644 --- a/app/static/app/js/components/UnitSelector.jsx +++ b/app/static/app/js/components/UnitSelector.jsx @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { systems, getPreferredUnitSystem, setPreferredUnitSystem } from '../classes/Units'; class UnitSelector extends React.Component { static propTypes = { @@ -9,19 +10,22 @@ class UnitSelector extends React.Component { super(props); this.state = { - system: window.getPreferredUnitSystem() + system: getPreferredUnitSystem() } + + // console.log(systems.metric.length(1.01).toString()); } handleChange = e => { this.setState({system: e.target.value}); - window.setPreferredUnitSystem(e.target.value); + setPreferredUnitSystem(e.target.value); }; render() { return ( ); } diff --git a/app/static/app/js/components/tests/Units.test.jsx b/app/static/app/js/components/tests/Units.test.jsx new file mode 100644 index 00000000..dd1929ab --- /dev/null +++ b/app/static/app/js/components/tests/Units.test.jsx @@ -0,0 +1,41 @@ +import { systems } from '../../classes/Units'; + +describe('Metric system', () => { + it('it should display units properly', () => { + + const { metric } = systems; + + const lengths = [ + [1, "1 m"], + [0.01, "1 cm"], + [0.0154, "1.5 cm"], + [0.99, "99 cm"], + [0.995555, "99.6 cm"], + [1.01, "1.01 m"], + [999, "999 m"], + [1000, "1 km"], + [1001, "1.001 km"], + [1000010, "1,000.01 km"], + [1000012.349, "1,000.01235 km"], + ]; + + lengths.forEach(l => { + expect(metric.length(l[0]).toString()).toBe(l[1]); + }); + + const areas = [ + [1, "1 m²"], + [9999, "9,999 m²"], + [10000, "1 ha"], + [11005, "1.1005 ha"], + [11005, "1.1005 ha"], + [999999, "99.9999 ha"], + [1000000, "1 km²"], + [1000000000, "1,000 km²"] + ]; + + areas.forEach(a => { + expect(metric.area(a[0]).toString()).toBe(a[1]); + }); + }) +}); \ No newline at end of file diff --git a/app/static/app/js/main.jsx b/app/static/app/js/main.jsx index 8b0b8692..3c219ef3 100644 --- a/app/static/app/js/main.jsx +++ b/app/static/app/js/main.jsx @@ -26,14 +26,6 @@ window.React = React; // Expose set locale function globally window.setLocale = setLocale; -// Expose to allow every part of the app to access this information -window.getPreferredUnitSystem = () => { - return localStorage.getItem("preferred_unit_system") || "metric"; -}; -window.setPreferredUnitSystem = (system) => { - localStorage.setItem("preferred_unit_system", system); -}; - $(function(){ PluginsAPI.App.triggerReady(); });