Go-Gin Geohashing

...

 

What is Geohashing?

Geohashing is a way to encode a point on the earth's surface into a short string of letters and digits and search for nearby geohashes to find nearby points.

Geohashing can be used in a variety of applications; some examples include:

Location-based services: Geohash can be used to encode and decode geographic coordinates in order to store and retrieve location data more efficiently. This can be used in applications such as mapping, navigation, and search engines.

Geocoding: Geohash can be used to encode geographic coordinates into a short, human-readable string that can be used as an address or location identifier.

Geofencing: Geohash can be used to create and manage virtual boundaries around specific geographic areas. This can be used in location-based marketing, security, and asset tracking applications.

Today, we will focus on implementing Geohash in real life using Go the Gin framework and Gorm for Database interaction to find nearby shops

To start, let's install the required packages.

 

go get gorm.io/gorm
go get github.com/gin-gonic/gin
go get gorm.io/driver/postgres
go get github.com/mmcloughlin/geohash

Let's create two structs, Business and Location, where Location will store the longitude and latitude of the business, representing its location, and Business will hold basic information about the business such as its name and website.

 

package api

 

import "gorm.io/gorm"

 

type Business struct {

   gorm.Model

   Name               string `gorm:"not null" json:"name,omitempty"`

   Website            string `gorm:"not null" json:"website,omitempty"`

   Address1           string `gorm:"not null" json:"address1,omitempty"`

   Location           []Location

}

 

 

type Location struct {

   gorm.Model

   Address    string  `gorm:"not null" json:"address"`

   Lat        float64 `gorm:"not null" json:"lat"`

   Lng        float64 `gorm:"not null" json:"lng"`

   BusinessID uint    `gorm:"business_id"`

}

 

Now that we have created the Business and Location structs, let's set up the database using the Gorm package as our ORM and the Postgres driver package, which will enable us to connect with the Postgres database.

Pass the Business and Location struct to the AutoMigrate function to create the two tables at Postgres.

 
package db

import (
"log"

"github.com/25-do/proessay/database/models"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)

func Init() *gorm.DB {
dbURL := "postgres://postgres:mypassword@localhost:5432/mydatabase"
db, err := gorm.Open(postgres.Open(dbURL), &gorm.Config{})
if err != nil {
log.Fatalln((err))
}
db.AutoMigrate(
&models.Business{},
&models.Location{},
)
return db
}
 

 

The function initializes a struct Business to store the request payload and then checks if the request payload is a valid JSON using the c.ShouldBind method. It sends a Bad Request response with the error message if it fails.

It then calculates the geohash of the business using the geo. Encode method, passing in the latitude and longitude from the BusinessForm struct.

It then creates a new instance of the Business struct with the data from the BusinessForm struct and the calculated geohash.

Finally, it uses the h.DB.Create method to insert the new business into the database and sends a success response with the created business data. If the insert fails, it sends a Bad Request response with the error message.

package api

 

import (

   "fmt"

   "net/http"

   "strconv"

 

   "github.com/craving_api/models"

   "github.com/gin-gonic/gin"

   geo "github.com/mmcloughlin/geohash"

)

func (h handler) AddBusiness(c *gin.Context) {

   var business Business

   if err := c.ShouldBind(&business); err != nil {

       c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

       return

   }

   geoh := geo.Encode(business.Lat, business.Lng)

   cli := Business{

       Name:               business.Name,

       Website:            business.Website,

       Address1:           business.Address1,

       Location:           []models.Location{{Lat: business.Lat, Lng: business.Lng}},

   }

   if err := h.DB.Create(&cli).Error; err != nil {

       c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

       return

   }

 

   c.JSON(http.StatusOk, gin.H{"data": cli})

 

}

Lets create a struct called inputAddress to store the request payload for the location , which we will use to query

type inputAddress struct {

   Lat      float64 `json:"lat"`

   Lng      float64 `json:"lng"`

}

The below function is for handling a request to get a paginated list of all shops based on the location specified in the request payload(inputAddress).

The function first initializes an empty slice of type models.Business to store the shop data and a struct inputAddress to store the request payload.

we then check if the request payload is valid JSON using the c.ShouldBindJSON method and if it fails, it sends a Bad Request response with the error message.

The function then calculates the location using the geo.Encode method, passing in the latitude and longitude from the request payload. It also calculates the nearby shops using the geo.Neighbors method, passing in the location.

Finally, it uses a database query to retrieve the name, address1 of all businesses in the nearby locations and stores the results in the shop slice. If the query fails, it sends a Bad Request response with the error message.

 

func (h handler) GetAllShopsPagination(c *gin.Context) {

   var shop []Business

   var addr inputAddress

   if err := c.ShouldBindJSON(&addr); err != nil {

       c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

       return

   }

 

   location := geo.Encode(addr.Lat, addr.Lng)

   nearByShops := geo.Neighbors(location)

   if err := h.DB.Table("businesses").

   Select("name, address1").

   Order("id").

   Where("geo_hash IN (?)", nearByShops).

   Find(&shop).Error; err != nil {

       c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

       return

   }

   c.JSON(http.StatusOk, gin.H{"data": shop})

}

 

In this short article, we saw how to implement the Geohashing algorithm using Go-gin framework. Later in future, we will do more advancement such us database relations image uploads, deployment, etc.

Leave a reply

0 Comments

Login to join the discussion