1
\$\begingroup\$

I have created simple algorithm to generate the new Location from given latitude and longitude.

// How to use : first call SetupLocation(InputLat,InputLon float64) to set base latitude and longitude.
// To genreate the location you can call RandomLatLong() to generate using seconds degree.
// And RandomLatLongMinute to genereate new location using minute degree.
package location

import "math/rand"

var lat, lon float64

type LocationDegree struct {
    Degree  int
    Minutes int
    Seconds int
}

// setup base location for generating the new randon lat, lon value.
func SetupLocation(InputLat, InputLon float64) {
    lat = InputLat
    lon = InputLon
}

func ToDegree(location float64) LocationDegree {
    // get degree
    degree := int(location)
    location = location - float64(degree)

    // get minute
    multiply := location * 60.0
    minutes := int(multiply)
    location = multiply - float64(minutes)

    // get seconds
    multiply = location * 60
    seconds := int(multiply)

    // check if the decimal is 9 or 8 we are gointo round up
    location = multiply - float64(seconds)
    if location > 0.5 {
        seconds += 1
    }

    return LocationDegree{
        Degree:  degree,
        Minutes: minutes,
        Seconds: seconds,
    }

}

func ToDecimal(locationDegree LocationDegree) float64 {
    var result float64
    result = float64(locationDegree.Seconds) / 60.0

    result += float64(locationDegree.Minutes)

    result = result / 60.0

    result += float64(locationDegree.Degree)

    return result
}

// add seconds to lat or lon so we can get new location.
// we created array location so we can randomly select lat and lon to add new seconds degree.
// getting seconds value using pseu-do random from given seconds.
// returning latitude and longitude in order.
func RandomLatLong(seconds int) (float64, float64) {
    location := []float64{lat, lon}
    randomIndex := rand.Intn(len(location))

    randomSeconds := rand.Intn(seconds)

    // convert the lat or lon depends on random selection to degree
    degreeLocation := ToDegree(location[randomIndex])
    degreeLocation.Seconds += randomSeconds

    // convert the degree to decimal degree and replace it to the current index
    degreeDecimal := ToDecimal(degreeLocation)
    location[randomIndex] = degreeDecimal

    // return lat and lon in orner
    return location[0], location[1]
}

// add minute to lat or lon so we can get new location.
// we created array location so we can randomly select lat and lon to add new seconds degree.
// getting minute value using pseu-do random from given minute.
// returning latitude and longitude in order.
func RandomLatLongMinute(minute int) (float64, float64) {

    location := []float64{lat, lon}
    randomIndex := rand.Intn(len(location))

    randomMinute := rand.Intn(minute)

    // convert the lat or lon depends on random selection to degree
    degreeLocation := ToDegree(location[randomIndex])
    degreeLocation.Minutes += randomMinute

    // convert the degree to decimal degree and replace it to the current index
    degreeDecimal := ToDecimal(degreeLocation)
    location[randomIndex] = degreeDecimal

    // return lat and lon in orner
    return location[0], location[1]
}

I have created this during my spare time, and wanted to give it a try. I know that this is not the best generated algorithm since it only add new random value to seconds or minutes and convert it to decimal again.

But I want to share this code and wanted to know if there are something that could be improved.

Source Code

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Bugs

You have a couple of bugs in your code which will crop up occasionally. The first I noticed is this:

// check if the decimal is 9 or 8 we are gointo round up
location = multiply - float64(seconds)
if location > 0.5 {
    seconds += 1
}

There's two things in there, firstly, the comment is a lie ;-) The code does nothing with 8 or 9 decimals... it rounds "half-up".

The second issue is that the rounding is inaccurate if/when the location is something like 59.8, which your code will round-up to 60. This is a problem, because it should round up to 0, and the minutes should be increased (and, worse, if the minutes is 59, you need to round up the degrees... for example, rounding the value 8°59'59.8" should be 9° 0' 0" seconds - but your code gives 8° 59' 60")

Half-up rounding

There's an easy trick for half-up rounding when dealing with integer-casting in programs, the trick is to add a half-unit to the source value before doing any calculations, and then truncating the result as an integer. For example, to half-up round a float value n to an int, you do int(n + 0.5) You can use this in your code quite nicely.

I would also reverse the logic you have in your toDegrees function... convert everything to seconds (half-up rounded in int) and then extract the values from that. Note that you can express the location in total seconds, total minutes, and total degrees, and then get the "remainder" (modulo) to get just the incremental part:

func ToDegree(location float64) LocationDegree {
    // get the location in seconds, rounded up if needed
    seconds := int(location * 60.0 * 60.0 + 0.5)
    minutes := seconds / 60
    degrees := seconds / (60 * 60)

    return LocationDegree{
        Degree:  degree,
        Minutes: minutes % 60,
        Seconds: seconds % 60,
    }
}

Simplified ToDecimal

Similarly, for the ToDecimal function, I would keep most of the calculation in integer space... Your code is not to bad, but can be improved:

func ToDecimal(locationDegree LocationDegree) float64 {
    var result float64
    result = float64(locationDegree.Seconds) / 60.0

    result += float64(locationDegree.Minutes)

    result = result / 60.0

    result += float64(locationDegree.Degree)

    return result
}

I would have it like:

func ToDecimal(locationDegree LocationDegree) float64 {
    totalSeconds := locationDegree.Degree * 60 * 60
    totalSeconds += locationDegree.Minutes * 60
    totalSeconds += locationDegree.Seconds
    return float64(totalSeconds) / (60.0 * 60.0)
}

General issues

Your code relies on the global values lat and lon. These should be declared in a struct, and your methods should be on that struct:

type Position struct {
    Lat float64
    Lon float64
}

func (p Position) RandomLatLong(seconds int) (float64, float64) {
        location := []float64{p.Lat, p.Lon}
    ....
}

Random adjustments

Your code converts the float64 positions to °'" in order to add an adjustment of minutes and seconds. There is no need to do that. Just have the adjustment in the format of a float64 and apply it directly. For example, for the seconds, have:

randomSeconds := float64(rand.Intn(seconds))

and for the minutes have:

randomMinutes := float64(rand.Intn(minutes)) * 60.0

Now you can add those directly to the value

\$\endgroup\$
0
\$\begingroup\$

I made some modification in the code to get the random Number :

// Get Random number from the maximal number given
// minimal number is 1 to max number
func getRandomNumber(max int) int {
    return rand.Intn(max-1) + 1 
}

and call it in every RandomLatLon function. As you can see the random number is getting the value from minimal which is 1 to max value.

and remove some assignment to variable.

from :

// convert the degree to decimal degree and replace it to the current index
    degreeDecimal := ToDecimal(degreeLocation)
    location[randomIndex] = degreeDecimal

to :

// convert the degree to decimal degree and replace it to the current index
    location[randomIndex] = ToDecimal(degreeLocation)

I think I dont need to assign the ToDecimal into new variable instead I directly replace the loacation value in the array. So it saves some memory for variable resource.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.