Skip to content

Commit

Permalink
Merge pull request #884 from Deltares/868-bounding-box-accuracy
Browse files Browse the repository at this point in the history
BoundingBox: improved the string conversion to ensure rounding
  • Loading branch information
ceesvoesenek authored Jun 18, 2024
2 parents e477633 + 578a6dc commit ee8eef7
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 15 deletions.
2 changes: 0 additions & 2 deletions src/components/map/BoundingBoxControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@

<script setup lang="ts">
import { computed } from 'vue'
import { BoundingBox, boundingBoxToString } from '@/services/useBoundingBox'
import DrawBoundingBoxControl from '@/components/map/DrawBoundingBoxControl.vue'
const boundingBox = defineModel<BoundingBox | null>('boundingBox', {
Expand Down
13 changes: 1 addition & 12 deletions src/components/wms/ElevationSlider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,7 @@ import VueSlider from 'vue-slider-component'
import 'vue-slider-component/theme/antd.css'
import { watchEffect } from 'vue'
import { scaleLinear } from 'd3-scale'
// Get decimal places of float (e.g. floatPrecision(54.6545) == 4)
function floatPrecision(a: number) {
if (!isFinite(a)) return 0
var e = 1,
p = 0
while (Math.round(a * e) / e !== a) {
e *= 10
p++
}
return p
}
import { floatPrecision } from '@/lib/utils/math'
interface Props {
modelValue: number
Expand Down
84 changes: 84 additions & 0 deletions src/lib/utils/math.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* @vitest-environment jsdom
*/

import { expect, test, describe } from 'vitest'
import {
floatPrecision,
roundToStepPrecision,
roundToDecimalPlaces,
} from './math'

describe('floatPrecision', () => {
test('with 4 decimal places', () => {
expect(floatPrecision(54.6545)).toEqual(4)
})

test('with 0 decimal places', () => {
expect(floatPrecision(54)).toEqual(0)
})

test('with 1 decimal place', () => {
expect(floatPrecision(54.1)).toEqual(1)
})

test('with negative number', () => {
expect(floatPrecision(-54.6545)).toEqual(4)
})
})

describe('roundToStepPrecision', () => {
test('round to 0.1 with whole number', () => {
expect(roundToStepPrecision(54, 0.1)).toEqual(54)
})

test('round to 0.01 with single decimal number', () => {
expect(roundToStepPrecision(54.1, 0.01)).toEqual(54.1)
})

test('round to 0.1', () => {
expect(roundToStepPrecision(54.6545, 0.1)).toEqual(54.7)
})

test('round to 0.01', () => {
expect(roundToStepPrecision(54.6545, 0.01)).toEqual(54.65)
})

test('round to 1', () => {
expect(roundToStepPrecision(54.6545, 1)).toEqual(55)
})

test('round to 10', () => {
expect(roundToStepPrecision(54.6545, 10)).toEqual(55)
})

test('round to 100', () => {
expect(roundToStepPrecision(54.6545, 100)).toEqual(55)
})

test('round with negative number', () => {
expect(roundToStepPrecision(-54.6545, 0.1)).toEqual(-54.7)
})
})

describe('roundToDecimalPlaces', () => {
test('round to 2 decimal places', () => {
expect(roundToDecimalPlaces(54.6545, 2)).toEqual(54.65)
})

test('round to 1 decimal place', () => {
expect(roundToDecimalPlaces(54.6545, 1)).toEqual(54.7)
})

test('round to 0 decimal places', () => {
expect(roundToDecimalPlaces(54.6545, 0)).toEqual(55)
})

test('round to negative decimal places', () => {
expect(roundToDecimalPlaces(54.6545, -1)).toEqual(NaN)
})

test('round with negative number', () => {
expect(roundToDecimalPlaces(-54.6545, 2)).toEqual(-54.65)
})
})
46 changes: 46 additions & 0 deletions src/lib/utils/math.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Get decimal places of float
* @param {number} a - float number
* @returns {number} The number of decimal places of the float number.
* @example
* floatPrecision(54.6545) // 4
*/
export function floatPrecision(a: number): number {
if (!isFinite(a)) return 0
var e = 1,
p = 0
while (Math.round(a * e) / e !== a) {
e *= 10
p++
}
return p
}

/**
* Round a number to the nearest step precision
* @param {number} value - The number to round.
* @param {number} step - The step to round to.
* @returns {number} The rounded number.
* @example
* roundToStepPrecision(54.6545, 0.1) // 54.7
*/
export function roundToStepPrecision(value: number, step: number): number {
const precision = floatPrecision(step)
return roundToDecimalPlaces(value, precision)
}

/**
* Round a number to a specified number of decimal places
* @param {number} value - The number to round.
* @param {number} decimalPlaces - The number of decimal places to round to.
* @returns {number} The rounded number.
* @example
* roundToDecimalPlaces(54.6545, 2) // 54.65
*/
export function roundToDecimalPlaces(
value: number,
decimalPlaces: number,
): number {
const num = Number(`${value}e+${decimalPlaces}`)
return Number(Math.round(num) + `e-${decimalPlaces}`)
}
5 changes: 4 additions & 1 deletion src/services/useBoundingBox/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { computed, ref } from 'vue'
import { roundToStepPrecision } from '@/lib/utils/math'

export interface BoundingBox {
lonMin: number
Expand Down Expand Up @@ -41,7 +42,9 @@ function roundBoundingBox(
const numSteps = doFloor
? Math.floor(absValue / step)
: Math.ceil(absValue / step)
return Math.sign(value) * numSteps * step

const result = Math.sign(value) * numSteps * step
return roundToStepPrecision(result, step)
}
// Round the bounding box to the specified step size.
return {
Expand Down

0 comments on commit ee8eef7

Please sign in to comment.