From 1fa92e18c81ca954fa151d9ffba57b2aecc9efb1 Mon Sep 17 00:00:00 2001
From: Since <ax20yhum@cip.cs.fau.de>
Date: Mon, 13 Nov 2017 18:59:40 +0100
Subject: [PATCH] Fix grid identification

An identificated grid is now indicated by the HasProjection flag.
---
 radolan.go   |  3 +++
 translate.go | 53 ++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/radolan.go b/radolan.go
index 44244cb..068c802 100644
--- a/radolan.go
+++ b/radolan.go
@@ -52,6 +52,7 @@ import (
 //	dBZ = 10 * log(Z)
 // Real world geographical coordinates (latitude, longitude) can be projected into the
 // coordinate system of the composite by using the translation method:
+//	// if c.HasProjection
 //	x, y := c.Translate(52.51861, 13.40833)	// Berlin (lat, lon)
 //
 //	rvp := c.At(int(x), int(y))				// Raw value (rvp-6)
@@ -75,6 +76,8 @@ type Composite struct {
 	Rx float64 // horizontal resolution in km/px
 	Ry float64 // vertical resolution in km/px
 
+	HasProjection bool // coordinate translation available
+
 	dataLength int // length of binary section in bytes
 
 	precision int   // multiplicator 10^precision for each raw value
diff --git a/translate.go b/translate.go
index 9c9e864..cfe635e 100644
--- a/translate.go
+++ b/translate.go
@@ -12,10 +12,21 @@ const (
 	junctionEast  = 10.0 // E
 )
 
-// cornerPoints returns corner coordinates of the national or extended european grid
+// isScaled returns true if the composite can be scaled to the given dimensions
+// while also maintaining the aspect ratio.
+func (c *Composite) isScaled(dx, dy int) bool {
+	epsilon := 0.00000001
+	return math.Abs(1-float64(c.Dy*dx)/float64(c.Dx*dy)) < epsilon
+}
+
+// errNoProjection means that the projection grid could not be identified.
+var errNoProjection = newError("cornerPoints", "warning: unable to identify grid")
+
+// cornerPoints returns corner coordinates of the national, extended or middle-european grid
 // based on the product label or resolution of the composite. The used values are
 // described in [1], [4] and [5].
-func (c *Composite) cornerPoints() (originTop, originLeft, edgeBottom, edgeRight float64) {
+// If an error is returned, translation methods will not work.
+func (c *Composite) cornerPoints() (originTop, originLeft, edgeBottom, edgeRight float64, err error) {
 	// national grid (pg) values described in [4]
 	if c.Product == "PG" {
 		originTop, originLeft = 54.6547, 01.9178 // N, E
@@ -24,22 +35,42 @@ func (c *Composite) cornerPoints() (originTop, originLeft, edgeBottom, edgeRight
 	}
 
 	// national grid values described in [1]
-	if c.Dx == c.Dy {
+	if c.isScaled(900, 900) {
 		originTop, originLeft = 54.5877, 02.0715 // N, E
 		edgeBottom, edgeRight = 47.0705, 14.6209 // N, E
 		return
 	}
 
-	// extended european grid described in [5]
-	originTop, originLeft = 56.5423, -0.8654 // N, E
-	edgeBottom, edgeRight = 43.8736, 18.2536 // N, E
+	// extended national grid described in [5]
+	if c.isScaled(900, 1100) {
+		originTop, originLeft = 55.5482, 03.0889 // N, E
+		edgeBottom, edgeRight = 46.1827, 15.4801 // N, E
+		return
+	}
+
+	// middle european grid described in [5]
+	if c.isScaled(1400, 1500) {
+		originTop, originLeft = 56.5423, -0.8654 // N, E
+		edgeBottom, edgeRight = 43.8736, 18.2536 // N, E
+		return
+	}
+
+	err = errNoProjection
 	return
 }
 
 // calibrateProjection initializes fields that are necessary for coordinate translation
 func (c *Composite) calibrateProjection() {
 	// get corner points
-	originTop, originLeft, edgeBottom, edgeRight := c.cornerPoints()
+	originTop, originLeft, edgeBottom, edgeRight, err := c.cornerPoints()
+	if err != nil {
+		nan := math.NaN()
+		c.Rx, c.Ry, c.offx, c.offy = nan, nan, nan, nan
+		return
+	}
+
+	// found matching projection rule
+	c.HasProjection = true
 
 	// set resolution to 1 km for calibration
 	c.Rx, c.Ry = 1.0, 1.0
@@ -54,8 +85,14 @@ func (c *Composite) calibrateProjection() {
 }
 
 // Translate translates geographical coordinates (latitude north, longitude east) to the
-// according data indices in the coordinate system of the composite. Procedures adapted from [1].
+// according data indices in the coordinate system of the composite.
+// NaN is returned when no projection is available. Procedures adapted from [1].
 func (c *Composite) Translate(north, east float64) (x, y float64) {
+	if !c.HasProjection {
+		x, y = math.NaN(), math.NaN()
+		return
+	}
+
 	rad := func(deg float64) float64 {
 		return deg * (math.Pi / 180.0)
 	}
-- 
GitLab