Skip to content

Commit

Permalink
Add earthquake event source API support of CEA and INGV
Browse files Browse the repository at this point in the history
  • Loading branch information
bclswl0827 committed Mar 20, 2024
1 parent f2b6d43 commit 708759a
Show file tree
Hide file tree
Showing 19 changed files with 411 additions and 66 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

Starting from v2.2.5, all notable changes to this project will be documented in this file.

## v2.11.8

- Add earthquake event source API support of CEA and INGV
- Reuse of int32 array encoding and decoding functions
- Specify the minimun TLS version to 1.0 in HTTP client

## v2.11.7

- Support earthquake event source API of Korea Meteorological Administration
- Add earthquake event source API support of KMA

## v2.11.6

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.11.7
v2.11.8
154 changes: 154 additions & 0 deletions app/v1/trace/cea.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package trace

import (
"bytes"
"fmt"
"strconv"
"strings"
"time"

"github.com/PuerkitoBio/goquery"
"github.com/anyshake/observer/utils/duration"
"github.com/anyshake/observer/utils/request"
)

type CEA_DASE struct {
DataSourceCache
}

func (c *CEA_DASE) Property() string {
return "Commissariat à l'Energie Atomique"
}

func (c *CEA_DASE) Fetch() ([]byte, error) {
if duration.Difference(time.Now(), c.Time) <= EXPIRATION {
return c.Cache, nil
}

res, err := request.GET(
"https://www-dase.cea.fr/evenement/derniers_evenements.php",
10*time.Second, time.Second, 3, false, nil,
)
if err != nil {
return nil, err
}

c.Time = time.Now()
c.Cache = make([]byte, len(res))
copy(c.Cache, res)

return res, nil
}

func (c *CEA_DASE) Parse(data []byte) (map[string]any, error) {
result := make(map[string]any)
result["data"] = make([]any, 0)

reader := bytes.NewBuffer(data)
doc, err := goquery.NewDocumentFromReader(reader)
if err != nil {
return nil, err
}

doc.Find(".arial11bleu").Each(func(i int, s *goquery.Selection) {
item := make(map[string]any)
var dateString string
s.Find("td").Each(func(i int, s *goquery.Selection) {
value := strings.TrimSpace(s.Text())
switch i {
case 0:
item["depth"] = -1
dateString = value
case 1:
item["timestamp"] = c.getTimestamp(fmt.Sprintf("%s %s", dateString, value))
case 2:
item["latitude"] = c.getLatitude(value)
item["longitude"] = c.getLongitude(value)
case 3:
item["event"] = value
item["region"] = value
case 4:
item["magnitude"] = c.getMagnitude(value)
}
})
result["data"] = append(result["data"].([]any), item)
})

return result, nil
}

func (c *CEA_DASE) Format(latitude, longitude float64, data map[string]any) ([]Event, error) {
var list []Event
for _, v := range data["data"].([]any) {
l := Event{
Verfied: false,
Latitude: v.(map[string]any)["latitude"].(float64),
Longitude: v.(map[string]any)["longitude"].(float64),
Depth: float64(v.(map[string]any)["depth"].(int)),
Event: v.(map[string]any)["event"].(string),
Region: v.(map[string]any)["region"].(string),
Timestamp: v.(map[string]any)["timestamp"].(int64),
Magnitude: v.(map[string]any)["magnitude"].(float64),
}
l.Distance = getDistance(latitude, l.Latitude, longitude, l.Longitude)
l.Estimation = getEstimation(l.Depth, l.Distance)

list = append(list, l)
}

return list, nil
}

func (c *CEA_DASE) List(latitude, longitude float64) ([]Event, error) {
res, err := c.Fetch()
if err != nil {
return nil, err
}

data, err := c.Parse(res)
if err != nil {
return nil, err
}

list, err := c.Format(latitude, longitude, data)
if err != nil {
return nil, err
}

return list, nil
}

func (c *CEA_DASE) getTimestamp(data string) int64 {
t, _ := time.Parse("02/01/2006 15:04:05", data)
return t.Add(-1 * time.Hour).UnixMilli()
}

func (c *CEA_DASE) getMagnitude(data string) float64 {
m := strings.Split(data, "=")
if len(m) > 1 {
magnitude, _ := strconv.ParseFloat(m[1], 64)
return magnitude
}

return 0
}

func (c *CEA_DASE) getLatitude(data string) float64 {
pos := strings.Split(data, ",")
if len(pos) > 1 {
latitude, _ := strconv.ParseFloat(pos[0], 64)
return latitude
}

return 0
}

func (c *CEA_DASE) getLongitude(data string) float64 {
pos := strings.Split(data, ",")
if len(pos) > 1 {
longitude, _ := strconv.ParseFloat(pos[1], 64)
return longitude
}

return 0
}
157 changes: 157 additions & 0 deletions app/v1/trace/ingv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package trace

import (
"encoding/csv"
"strconv"
"strings"
"time"

"github.com/anyshake/observer/utils/duration"
"github.com/anyshake/observer/utils/request"
)

type INGV struct {
DataSourceCache
}

func (c *INGV) Property() string {
return "Istituto nazionale di geofisica e vulcanologia"
}

func (c *INGV) Fetch() ([]byte, error) {
if duration.Difference(time.Now(), c.Time) <= EXPIRATION {
return c.Cache, nil
}

res, err := request.GET(
"https://webservices.ingv.it/fdsnws/event/1/query?minmag=-1&format=text&timezone=UTC",
10*time.Second, time.Second, 3, false, nil,
)
if err != nil {
return nil, err
}

c.Time = time.Now()
c.Cache = make([]byte, len(res))
copy(c.Cache, res)

return res, nil
}

func (c *INGV) Parse(data []byte) (map[string]any, error) {
result := make(map[string]any)
result["data"] = make([]any, 0)

csvDataStr := strings.ReplaceAll(string(data), "|", ",")
reader := csv.NewReader(strings.NewReader(csvDataStr))
records, err := reader.ReadAll()
if err != nil {
return nil, err
}

for _, record := range records[1:] {
item := make(map[string]any)
for i, v := range record {
switch i {
case 1:
item["timestamp"] = c.getTimestamp(v)
case 2:
item["latitude"] = c.getLatitude(v)
case 3:
item["longitude"] = c.getLongitude(v)
case 4:
item["depth"] = c.getDepth(v)
case 10:
item["magnitude"] = c.getMagnitude(v)
case 12:
item["event"] = v
item["region"] = v
}
}
result["data"] = append(result["data"].([]any), item)
}

return result, nil
}

func (c *INGV) Format(latitude, longitude float64, data map[string]any) ([]Event, error) {
var list []Event
for _, v := range data["data"].([]any) {
l := Event{
Verfied: true,
Latitude: v.(map[string]any)["latitude"].(float64),
Longitude: v.(map[string]any)["longitude"].(float64),
Depth: v.(map[string]any)["depth"].(float64),
Event: v.(map[string]any)["event"].(string),
Region: v.(map[string]any)["region"].(string),
Timestamp: v.(map[string]any)["timestamp"].(int64),
Magnitude: v.(map[string]any)["magnitude"].(float64),
}
l.Distance = getDistance(latitude, l.Latitude, longitude, l.Longitude)
l.Estimation = getEstimation(l.Depth, l.Distance)

list = append(list, l)
}

return list, nil
}

func (c *INGV) List(latitude, longitude float64) ([]Event, error) {
res, err := c.Fetch()
if err != nil {
return nil, err
}

data, err := c.Parse(res)
if err != nil {
return nil, err
}

list, err := c.Format(latitude, longitude, data)
if err != nil {
return nil, err
}

return list, nil
}

func (c *INGV) getTimestamp(data string) int64 {
t, _ := time.Parse("2006-01-02T15:04:05.000000", data)
return t.UnixMilli()
}

func (c *INGV) getMagnitude(data string) float64 {
m, err := strconv.ParseFloat(data, 64)
if err == nil {
return m
}

return 0
}

func (c *INGV) getDepth(data string) float64 {
d, err := strconv.ParseFloat(data, 64)
if err == nil {
return d
}

return 0
}

func (c *INGV) getLatitude(data string) float64 {
lat, err := strconv.ParseFloat(data, 64)
if err == nil {
return lat
}

return 0
}

func (c *INGV) getLongitude(data string) float64 {
lng, err := strconv.ParseFloat(data, 64)
if err == nil {
return lng
}

return 0
}
18 changes: 10 additions & 8 deletions app/v1/trace/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ import (
// @Success 200 {object} response.HttpResponse{data=[]Event} "Successfully read the list of earthquake events"
func (t *Trace) RegisterModule(rg *gin.RouterGroup, options *app.ServerOptions) {
sources := map[string]DataSource{
"CWA": &CWA{},
"HKO": &HKO{},
"JMA": &JMA{},
"KMA": &KMA{},
"CEIC": &CEIC{},
"USGS": &USGS{},
"SCEA_E": &SCEA_E{},
"SCEA_B": &SCEA_B{},
"CWA": &CWA{},
"HKO": &HKO{},
"JMA": &JMA{},
"KMA": &KMA{},
"CEIC": &CEIC{},
"USGS": &USGS{},
"INGV": &INGV{},
"SCEA_E": &SCEA_E{},
"SCEA_B": &SCEA_B{},
"CEA_DASE": &CEA_DASE{},
}

rg.POST("/trace", func(c *gin.Context) {
Expand Down
10 changes: 10 additions & 0 deletions driver/dao/insert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dao

import (
"github.com/anyshake/observer/publisher"
"gorm.io/gorm"
)

func Insert(db *gorm.DB, gp *publisher.Geophone) error {
return db.Table(DB_TABLENAME).Create(&dbRecord{Geophone: *gp}).Error
}
7 changes: 7 additions & 0 deletions driver/dao/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dao

import "gorm.io/gorm"

func Migrate(db *gorm.DB) error {
return db.Table(DB_TABLENAME).AutoMigrate(&dbRecord{})
}
8 changes: 0 additions & 8 deletions driver/dao/operation.go → driver/dao/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ import (
"gorm.io/gorm"
)

func Migrate(db *gorm.DB) error {
return db.Table(DB_TABLENAME).AutoMigrate(&dbRecord{})
}

func Insert(db *gorm.DB, gp *publisher.Geophone) error {
return db.Table(DB_TABLENAME).Create(&dbRecord{Geophone: *gp}).Error
}

func Query(db *gorm.DB, start, end int64) ([]publisher.Geophone, error) {
var records []dbRecord
err := db.Table(DB_TABLENAME).Select("ts, ehz, ehe, ehn").Where("ts >= ? AND ts <= ?", start, end).Scan(&records).Error
Expand Down
Loading

0 comments on commit 708759a

Please sign in to comment.