Skip to content

Commit

Permalink
Add location service (micro#29)
Browse files Browse the repository at this point in the history
* Add location service

* Update README.md
  • Loading branch information
asim authored Nov 17, 2020
1 parent 8f8e9a3 commit 25ae1f2
Show file tree
Hide file tree
Showing 15 changed files with 869 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/golang/protobuf v1.4.3
github.com/google/uuid v1.1.2
github.com/gosimple/slug v1.9.0
github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711
github.com/micro/dev v0.0.0-20201111162228-80c2b20de2db
github.com/micro/go-micro/v2 v2.9.1 // indirect
github.com/micro/micro/v3 v3.0.0-beta.7
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 h1:Oi8hPOZX0gaM2sPVXse2bMpfOjP47a7O61YuB6Z4sGk=
github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711/go.mod h1:+v2qJ3UZe4q2GfgZO4od004F/cMgJbmPSs7dD/ZMUkY=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
Expand Down
2 changes: 2 additions & 0 deletions location/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

location
3 changes: 3 additions & 0 deletions location/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine
ADD location /location
ENTRYPOINT [ "/location" ]
22 changes: 22 additions & 0 deletions location/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

GOPATH:=$(shell go env GOPATH)
.PHONY: init
init:
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
.PHONY: proto
proto:
protoc --proto_path=. --micro_out=. --go_out=:. proto/location.proto

.PHONY: build
build:
go build -o location *.go

.PHONY: test
test:
go test -v ./... -cover

.PHONY: docker
docker:
docker build . -t location:latest
23 changes: 23 additions & 0 deletions location/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Location Service

A realtime GPS location tracking and search service

Generated with

```
micro new location
```

## Usage

Generate the proto code

```
make proto
```

Run the service

```
micro run .
```
104 changes: 104 additions & 0 deletions location/domain/domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package domain

import (
"sync"

geo "github.com/hailocab/go-geoindex"
"github.com/micro/micro/v3/service/errors"
common "github.com/micro/services/location/proto"
)

var (
mtx sync.RWMutex
defaultIndex = geo.NewPointsIndex(geo.Km(0.5))
)

type Entity struct {
ID string
Type string
Latitude float64
Longitude float64
Timestamp int64
}

func (e *Entity) Id() string {
return e.ID
}

func (e *Entity) Lat() float64 {
return e.Latitude
}

func (e *Entity) Lon() float64 {
return e.Longitude
}

func (e *Entity) ToProto() *common.Entity {
return &common.Entity{
Id: e.ID,
Type: e.Type,
Location: &common.Point{
Latitude: e.Latitude,
Longitude: e.Longitude,
Timestamp: e.Timestamp,
},
}
}

func ProtoToEntity(e *common.Entity) *Entity {
return &Entity{
ID: e.Id,
Type: e.Type,
Latitude: e.Location.Latitude,
Longitude: e.Location.Longitude,
Timestamp: e.Location.Timestamp,
}
}

func Read(id string) (*Entity, error) {
mtx.RLock()
defer mtx.RUnlock()

p := defaultIndex.Get(id)
if p == nil {
return nil, errors.NotFound("location.read", "Not found")
}

entity, ok := p.(*Entity)
if !ok {
return nil, errors.InternalServerError("location.read", "Error reading entity")
}

return entity, nil
}

func Save(e *Entity) {
mtx.Lock()
defaultIndex.Add(e)
mtx.Unlock()
}

func Search(typ string, entity *Entity, radius float64, numEntities int) []*Entity {
mtx.RLock()
defer mtx.RUnlock()

points := defaultIndex.KNearest(entity, numEntities, geo.Meters(radius), func(p geo.Point) bool {
e, ok := p.(*Entity)
if !ok || e.Type != typ {
return false
}
return true
})

var entities []*Entity

for _, point := range points {
e, ok := point.(*Entity)
if !ok {
continue
}
entities = append(entities, e)
}

return entities
}
3 changes: 3 additions & 0 deletions location/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package main

//go:generate make proto
67 changes: 67 additions & 0 deletions location/handler/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package handler

import (
"context"
"log"

"github.com/micro/micro/v3/service"
"github.com/micro/micro/v3/service/errors"
"github.com/micro/services/location/domain"
loc "github.com/micro/services/location/proto"
"github.com/micro/services/location/subscriber"
)

type Location struct{}

func (l *Location) Read(ctx context.Context, req *loc.ReadRequest, rsp *loc.ReadResponse) error {
log.Print("Received Location.Read request")

id := req.Id

if len(id) == 0 {
return errors.BadRequest("location.read", "Require Id")
}

entity, err := domain.Read(id)
if err != nil {
return err
}

rsp.Entity = entity.ToProto()

return nil
}

func (l *Location) Save(ctx context.Context, req *loc.SaveRequest, rsp *loc.SaveResponse) error {
log.Print("Received Location.Save request")

entity := req.GetEntity()

if entity.GetLocation() == nil {
return errors.BadRequest("location.save", "Require location")
}

p := service.NewEvent(subscriber.Topic)
if err := p.Publish(ctx, entity); err != nil {
return errors.InternalServerError("location.save", err.Error())
}

return nil
}

func (l *Location) Search(ctx context.Context, req *loc.SearchRequest, rsp *loc.SearchResponse) error {
log.Print("Received Location.Search request")

entity := &domain.Entity{
Latitude: req.Center.Latitude,
Longitude: req.Center.Longitude,
}

entities := domain.Search(req.Type, entity, req.Radius, int(req.NumEntities))

for _, e := range entities {
rsp.Entities = append(rsp.Entities, e.ToProto())
}

return nil
}
24 changes: 24 additions & 0 deletions location/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"log"

"github.com/micro/micro/v3/service"
"github.com/micro/services/location/handler"
pb "github.com/micro/services/location/proto"
"github.com/micro/services/location/subscriber"
)

func main() {
location := service.New(
service.Name("location"),
)

pb.RegisterLocationHandler(location.Server(), new(handler.Location))

service.Subscribe(subscriber.Topic, new(subscriber.Location))

if err := location.Run(); err != nil {
log.Fatal(err)
}
}
1 change: 1 addition & 0 deletions location/micro.mu
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
service location
Loading

0 comments on commit 25ae1f2

Please sign in to comment.