Skip to content

Commit

Permalink
Fix station filter logic for nodes having no children.
Browse files Browse the repository at this point in the history
  • Loading branch information
junghao committed Feb 23, 2023
1 parent 0851881 commit 0cc80d8
Show file tree
Hide file tree
Showing 2 changed files with 320 additions and 76 deletions.
143 changes: 69 additions & 74 deletions cmd/fdsn-ws/fdsn_station.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,10 @@ func (r *FDSNStationXML) marshalText(levelVal int) *bytes.Buffer {
net.StartDate.MarshalFormatText(), net.EndDate.MarshalFormatText(),
net.TotalNumberStations))
} else {
if levelVal == STATION_LEVEL_STATION && len(net.Station) == 0 {
// Write Network name only
by.WriteString(fmt.Sprintf("%s|||||||\n", net.Code))
}
for s := 0; s < len(net.Station); s++ {
sta := &net.Station[s]
if levelVal == STATION_LEVEL_STATION {
Expand All @@ -583,6 +587,10 @@ func (r *FDSNStationXML) marshalText(levelVal int) *bytes.Buffer {
sta.Latitude.Value, sta.Longitude.Value, sta.Elevation.Value,
sta.Site.Name, sta.StartDate.MarshalFormatText(), sta.EndDate.MarshalFormatText()))
} else {
if len(sta.Channel) == 0 {
// Write Station name only
by.WriteString(fmt.Sprintf("%s|%s||||||\n", net.Code, sta.Code))
}
for c := 0; c < len(sta.Channel); c++ {
cha := &sta.Channel[c]
var frequency float64
Expand Down Expand Up @@ -610,18 +618,18 @@ func (r *FDSNStationXML) marshalText(levelVal int) *bytes.Buffer {
}

func (r *FDSNStationXML) doFilter(params []fdsnStationV1Search) bool {
ns := make([]NetworkType, 0)

resultNetworks := make([]NetworkType, 0)
for _, n := range r.Network {
if n.doFilter(params) {
ns = append(ns, n)
resultNetworks = append(resultNetworks, n)
}
}

r.Network = ns
r.Network = resultNetworks

// note: we're not considering networks without children because it's unlikely to happen

if len(ns) == 0 {
// No result ( no "Network" node )
if len(resultNetworks) == 0 {
return false
}

Expand All @@ -638,7 +646,7 @@ func (r *FDSNStationXML) doFilter(params []fdsnStationV1Search) bool {
func (n *NetworkType) doFilter(params []fdsnStationV1Search) bool {
n.TotalNumberStations = len(n.Station)
matchedParams := make([]fdsnStationV1Search, 0)
ss := make([]StationType, 0)
resultStations := make([]StationType, 0)

for _, p := range params {
if !p.validStartEnd(time.Time(n.StartDate), time.Time(n.EndDate), STATION_LEVEL_NETWORK) {
Expand All @@ -655,46 +663,34 @@ func (n *NetworkType) doFilter(params []fdsnStationV1Search) bool {
return false
}

if len(n.Station) == 0 {
// for network nodes without children:
// 1. If the query parameter stops at network level, then the match is done
// 2. Otherwise, we're unable to get a match because of empty children (thus returning false)
for _, p := range matchedParams {
if p.StationReg == nil && p.ChannelReg == nil && p.LocationReg == nil {
return true
}
}
return false
}

for _, s := range n.Station {
if s.doFilter(matchedParams) {
ss = append(ss, s)
}
}

if len(ss) == 0 {
// Special case: when requested level is deeper than this level,
// but no child node from this node, then we should skip this node.
if params[0].LevelValue > STATION_LEVEL_NETWORK {
return false
}

// NOTE: the long description under is unlikely to happen since we only got 1 network.
// However I still included the logic.
// ---
// Normally this network is included since it has met the query parameters for network.
// However, there's another case:
// e.g. "query?station=ZZZZ&level=network"
// This kind of query makes the network test bypassed due to no query parameter for network,
// but when there's no child node for this network we should skip this network.
// In short:
// when there's no child node and there's query parameter for station, channel or location,
// this network is excluded.
for _, p := range params {
if p.StationReg != nil || p.ChannelReg != nil || p.LocationReg != nil {
return false
}
resultStations = append(resultStations, s)
}
}

n.SelectedNumberStations = len(ss)
n.Station = ss
n.SelectedNumberStations = len(resultStations)
n.Station = resultStations

return true
// this node only valid if the children matches any query
return n.SelectedNumberStations > 0
}

func (s *StationType) doFilter(params []fdsnStationV1Search) bool {
s.TotalNumberChannels = len(s.Channel)
cs := make([]ChannelType, 0)
resultChannels := make([]ChannelType, 0)

matchedParams := make([]fdsnStationV1Search, 0)

Expand All @@ -705,10 +701,10 @@ func (s *StationType) doFilter(params []fdsnStationV1Search) bool {
if p.StationReg != nil && !matchAnyRegex(s.Code, p.StationReg) {
continue
}
if !p.validLatLng(s.Latitude.Value, s.Longitude.Value) {
if !p.validLatLng(s.Latitude, s.Longitude) {
continue
}
if !p.validBounding(s.Latitude.Value, s.Longitude.Value) {
if !p.validBounding(s.Latitude, s.Longitude) {
continue
}
matchedParams = append(matchedParams, p)
Expand All @@ -719,38 +715,30 @@ func (s *StationType) doFilter(params []fdsnStationV1Search) bool {
return false
}

for _, c := range s.Channel {
if c.doFilter(matchedParams) {
cs = append(cs, c)
if len(s.Channel) == 0 {
// for station nodes without children:
// 1. If the query parameter stops at station level, then the match is done
// 2. Otherwise, we're unable to get a match (thus returning false)
for _, p := range matchedParams {
if p.ChannelReg == nil && p.LocationReg == nil {
return true
}
}
}

if len(cs) == 0 {
// Special case: when requested level is deeper than this level,
// but no child node from this node, then we should skip this node.
if params[0].LevelValue > STATION_LEVEL_STATION {
return false
}
return false
}

// Normally this stations is included since it has met the query parameters for station.
// However, there's another case:
// e.g. "query?channel=BTT"
// This kind of query makes the station test bypassed due to no query parameter for station,
// but when there's no child node for this station we should skip this station.
// In conclusion:
// when there's no sub child and there's query parameter for channel or location,
// this station is excluded.
for _, p := range params {
if p.ChannelReg != nil || p.LocationReg != nil {
return false
}
for _, c := range s.Channel {
if c.doFilter(matchedParams) {
resultChannels = append(resultChannels, c)
}
}

s.SelectedNumberChannels = len(cs)
s.Channel = cs
s.SelectedNumberChannels = len(resultChannels)
s.Channel = resultChannels

return true
// this node only valid if the children matches any query
return s.SelectedNumberChannels > 0
}

func (c *ChannelType) doFilter(params []fdsnStationV1Search) bool {
Expand All @@ -764,10 +752,10 @@ func (c *ChannelType) doFilter(params []fdsnStationV1Search) bool {
if p.LocationReg != nil && !matchAnyRegex(c.LocationCode, p.LocationReg) {
continue
}
if !p.validLatLng(c.Latitude.Value, c.Longitude.Value) {
if !p.validLatLng(c.Latitude, c.Longitude) {
continue
}
if !p.validBounding(c.Latitude.Value, c.Longitude.Value) {
if !p.validBounding(c.Latitude, c.Longitude) {
continue
}

Expand Down Expand Up @@ -827,33 +815,40 @@ func (v fdsnStationV1Search) validStartEnd(start, end time.Time, level int) bool
return true
}

func (v fdsnStationV1Search) validLatLng(latitude, longitude float64) bool {
if v.MinLatitude != math.MaxFloat64 && latitude < v.MinLatitude {
func (v fdsnStationV1Search) validLatLng(latitude *LatitudeType, longitude *LongitudeType) bool {
if v.MinLatitude != math.MaxFloat64 && (latitude == nil || latitude.Value < v.MinLatitude) {
// request to check latitude:
// 1. this node doesn't have latitude -> check failed
// 2. the value fall outside the range -> check failed
// (similar logics apply for cases below)
return false
}

if v.MaxLatitude != math.MaxFloat64 && latitude > v.MaxLatitude {
if v.MaxLatitude != math.MaxFloat64 && (latitude == nil || latitude.Value > v.MaxLatitude) {
return false
}

if v.MinLongitude != math.MaxFloat64 && longitude < v.MinLongitude {
if v.MinLongitude != math.MaxFloat64 && (longitude == nil || longitude.Value < v.MinLongitude) {
return false
}

if v.MaxLongitude != math.MaxFloat64 && longitude > v.MaxLongitude {
if v.MaxLongitude != math.MaxFloat64 && (longitude == nil || longitude.Value > v.MaxLongitude) {
return false
}

return true
}

func (v fdsnStationV1Search) validBounding(latitude, longitude float64) bool {
func (v fdsnStationV1Search) validBounding(latitude *LatitudeType, longitude *LongitudeType) bool {
if v.Latitude == math.MaxFloat64 {
// not using bounding circle
return true
}

d, _, err := wgs84.DistanceBearing(v.Latitude, v.Longitude, latitude, longitude)
if latitude == nil || longitude == nil {
// requested bounding circle, but this node doesn't have lat/lon
return false
}
d, _, err := wgs84.DistanceBearing(v.Latitude, v.Longitude, latitude.Value, longitude.Value)
if err != nil {
log.Printf("Error checking bounding:%s\n", err.Error())
return false
Expand Down
Loading

0 comments on commit 0cc80d8

Please sign in to comment.