2020import org .slf4j .LoggerFactory ;
2121
2222import javax .annotation .Nonnull ;
23+ import javax .annotation .Nullable ;
24+
25+ class DecodeLimits {
26+
27+ // construct empty
28+ public void DecodeLimits () {
29+ defined = false ;
30+ }
31+
32+ // generate upper and lower limits based on x and y, and delta's
33+ public void setFromDelta (final double y , final double x , final double yDelta , final double xDelta ) {
34+ if (xDelta < 0 ) {
35+ maxx = x ;
36+ minx = x + xDelta ;
37+ }
38+ else {
39+ minx = x ;
40+ maxx = x + xDelta ;
41+ }
42+ if (yDelta < 0 ) {
43+ maxy = y ;
44+ miny = y + yDelta ;
45+ }
46+ else {
47+ miny = y ;
48+ maxy = y + yDelta ;
49+ }
50+ defined = true ;
51+ }
52+
53+ // get minimum y (inclusive)
54+ public double getMinY () {
55+ assert defined ;
56+ return miny ;
57+ }
58+
59+ // get maximum y (exclusive)
60+ public double getMaxY () {
61+ assert defined ;
62+ return maxy ;
63+ }
64+
65+ // get minimum x (inclusive)
66+ public double getMinX () {
67+ assert defined ;
68+ return minx ;
69+ }
70+
71+ // get maximum x (exclusive)
72+ public double getMaxX () {
73+ assert defined ;
74+ return maxx ;
75+ }
76+
77+ // cut X to specified maximum, if necessary. Returns false if cut results in an empty range
78+ public boolean cutMaxX (int thatMaxX ) {
79+ if (maxx >= thatMaxX ) { maxx = thatMaxX ; }
80+ return (maxx > minx );
81+ }
82+
83+ // cut Y to specified maximum, if necessary. Returns false if cut results in an empty range
84+ public boolean cutMaxY (int thatMaxY ) {
85+ if (maxy >= thatMaxY ) { maxy = thatMaxY ; }
86+ return (maxy > miny );
87+ }
88+
89+ // cut Y to specified minimum, if necessary. Returns false if cut results in an empty range
90+ public boolean cutMinY (int thatMinY ) {
91+ if (miny <= thatMinY ) { miny = thatMinY ; }
92+ return (maxy >= miny );
93+ }
94+
95+ // cut X to specified minimum, if necessary. Returns false if cut results in an empty range
96+ public boolean cutMinX (int thatMinX ) {
97+ if (minx <= thatMinX ) { minx = thatMinX ; }
98+ return (maxx >= minx );
99+ }
100+
101+ @ Nonnull
102+ @ Override
103+ public String toString () {
104+ return defined ? ("(" + miny + ", " + minx + ")-(" + maxy + ", " + maxx + ")" ) : "undefined" ;
105+ }
106+
107+ // private parts
108+ private boolean defined ;
109+ private double miny ;
110+ private double maxy ;
111+ private double minx ;
112+ private double maxx ;
113+ }
23114
24115class Decoder {
25116 private static final Logger LOG = LoggerFactory .getLogger (Decoder .class );
@@ -44,8 +135,6 @@ static Point decode(@Nonnull final String argMapcode,
44135 String mapcode = argMapcode ;
45136 Territory territory = argTerritory ;
46137
47- // In case of error, result.isDefined() is false.
48- Point result = Point .undefined ();
49138 String extrapostfix = "" ;
50139
51140 final int minpos = mapcode .indexOf ('-' );
@@ -59,7 +148,8 @@ static Point decode(@Nonnull final String argMapcode,
59148
60149 mapcode = aeuUnpack (mapcode ).trim ();
61150 if (mapcode .isEmpty ()) {
62- return result ; // failed to decode!
151+ LOG .error ("Failed to aeuUnpack {}" , argMapcode );
152+ return Point .undefined (); // failed to aeuUnpack
63153 }
64154
65155 final int incodexlen = mapcode .length () - 1 ;
@@ -90,6 +180,9 @@ static Point decode(@Nonnull final String argMapcode,
90180 final int incodexhi = mapcode .indexOf ('.' );
91181 final int incodex = (incodexhi * 10 ) + (incodexlen - incodexhi );
92182
183+ // In case of error, result.isDefined() is false.
184+ Point result = Point .undefined ();
185+
93186 for (int i = from ; i <= upto ; i ++) {
94187 final int codexi = Data .calcCodex (i );
95188 if (Data .recType (i ) == 0 ) {
@@ -104,24 +197,58 @@ static Point decode(@Nonnull final String argMapcode,
104197 } else {
105198 // i = grid without headerletter
106199 if ((codexi == incodex ) || ((incodex == 22 ) && (codexi == 21 ))) {
200+ DecodeLimits decodeLimits = new DecodeLimits ();
107201 result = decodeGrid (mapcode ,
108202 Data .getBoundaries (i ).getMinX (), Data .getBoundaries (i ).getMinY (),
109203 Data .getBoundaries (i ).getMaxX (), Data .getBoundaries (i ).getMaxY (),
110- i , extrapostfix );
204+ i , extrapostfix , decodeLimits );
111205
112206 if (Data .isRestricted (i ) && result .isDefined ()) {
113207 boolean fitssomewhere = false ;
114208 int j ;
115- for (j = upto - 1 ; j >= from ; j --) {
209+ for (j = i - 1 ; j >= from ; j --) {
116210 if (!Data .isRestricted (j )) {
117211 final int xdiv8 = Common .xDivider (Data .getBoundaries (j ).getMinY (),
118212 Data .getBoundaries (j ).getMaxY ()) / 4 ;
119- if (Data .getBoundaries (j ).extendBounds ( xdiv8 , 60 ). containsPoint (result )) {
213+ if (Data .getBoundaries (j ).containsPoint (result )) {
120214 fitssomewhere = true ;
121215 break ;
122216 }
123217 }
124218 }
219+
220+ if (!fitssomewhere ) { // FORCE_RECODE
221+ for (j = from ; j < i ; j ++) { // try all smaller rectangles j
222+ if (!Data .isRestricted (j )) {
223+ int minx = Data .getBoundaries (j ).getMinX ();
224+ int maxx = Data .getBoundaries (j ).getMaxX ();
225+ if ((maxx < 0 ) && (result .getLonDeg () > 0 )) {
226+ minx += 360000000 ;
227+ maxx += 360000000 ;
228+ }
229+ final int miny = Data .getBoundaries (j ).getMinY ();
230+ final int maxy = Data .getBoundaries (j ).getMaxY ();
231+
232+ // force pt within decodeLimits
233+ Point pt = Point .fromPoint (result );
234+ if (miny <= decodeLimits .getMaxY ()) { pt .setMinLatToMicroDeg (miny ); }
235+ if (maxy > decodeLimits .getMinY ()) { pt .setMaxLatToMicroDeg (maxy ); }
236+ if (minx <= decodeLimits .getMaxX ()) { pt .setMinLonToMicroDeg (minx ); }
237+ if (maxx > decodeLimits .getMinX ()) { pt .setMaxLonToMicroDeg (maxx ); }
238+
239+ // better?
240+ if ( pt .getLatDeg () > decodeLimits .getMinY () && pt .getLatMicroDeg () < decodeLimits .getMaxY () &&
241+ pt .getLonDeg () > decodeLimits .getMinX () && pt .getLonMicroDeg () < decodeLimits .getMaxX () &&
242+ Data .getBoundaries (j ).containsPoint (pt ))
243+ {
244+ result = Point .fromPoint (pt );
245+ fitssomewhere = true ;
246+ break ;
247+ }
248+ }
249+ }
250+ } //FORCE_RECODE
251+
125252 if (!fitssomewhere ) {
126253 result .setUndefined ();
127254 }
@@ -135,7 +262,7 @@ static Point decode(@Nonnull final String argMapcode,
135262 result = decodeGrid (mapcode .substring (1 ),
136263 Data .getBoundaries (i ).getMinX (), Data .getBoundaries (i ).getMinY (),
137264 Data .getBoundaries (i ).getMaxX (), Data .getBoundaries (i ).getMaxY (),
138- i , extrapostfix );
265+ i , extrapostfix , null );
139266 break ;
140267 }
141268 }
@@ -158,15 +285,27 @@ static Point decode(@Nonnull final String argMapcode,
158285
159286 // LIMIT_TO_OUTRECT : make sure it fits the country
160287 if (ccode != CCODE_EARTH ) {
161- final SubArea mapcoderRect = Data .getBoundaries (upto ); // find
162- // encompassing
163- // rect
164- final int xdiv8 = Common .xDivider (mapcoderRect . getMinY (), mapcoderRect . getMaxY () ) / 4 ;
288+ final SubArea mapcoderRect = Data .getBoundaries (upto );
289+ final int miny = mapcoderRect . getMinY ();
290+ final int maxy = mapcoderRect . getMaxY ();
291+ final int xdiv8 = Common .xDivider (miny , maxy ) / 4 ;
165292 // should be /8 but there's some extra margin
166293 if (!mapcoderRect .extendBounds (xdiv8 , 60 ).containsPoint (result )) {
167294 result .setUndefined (); // decodes outside the official territory
168295 // limit
169296 }
297+ else { // FORCE_RECODE
298+ result .setMinLatToMicroDeg (miny );
299+ result .setMaxLatToMicroDeg (maxy );
300+ int minx = mapcoderRect .getMinX ();
301+ int maxx = mapcoderRect .getMaxX ();
302+ if ((minx > 0 ) && (result .getLonDeg () < 0 )) {
303+ minx -= 360000000 ;
304+ maxx -= 360000000 ;
305+ }
306+ result .setMinLonToMicroDeg (minx );
307+ result .setMaxLonToMicroDeg (maxx );
308+ } // FORCE_RECODE
170309 }
171310 }
172311
@@ -267,7 +406,7 @@ public Unicode2Ascii(final char min, final char max, @Nonnull final String conve
267406
268407 @ Nonnull
269408 private static Point decodeGrid (final String str , final int minx , final int miny , final int maxx , final int maxy ,
270- final int m , final String extrapostfix ) {
409+ final int m , final String extrapostfix , DecodeLimits decodeLimits ) {
271410 // for a well-formed result, and integer variables
272411 String result = str ;
273412 int relx ;
@@ -342,7 +481,16 @@ private static Point decodeGrid(final String str, final int minx, final int miny
342481 final int cornery = rely + (dify * dividery );
343482 final int cornerx = relx + (difx * dividerx );
344483
345- return decodeExtension (cornery , cornerx , dividerx << 2 , dividery , extrapostfix , 0 ); // grid
484+ Point pt = Point .fromMicroDeg (cornery ,cornerx );
485+ if (!(Data .getBoundaries (m ).containsPoint (pt ))) {
486+ LOG .info ("*** decodeGrid({}) : {} not in {}" , str , pt , Data .getBoundaries (m ));
487+ return Point .undefined (); // already out of range
488+ }
489+
490+ final int decodeMaxx = ((relx + xgridsize ) < maxx ) ? (relx + xgridsize ) : maxx ;
491+ final int decodeMaxy = ((rely + ygridsize ) < maxy ) ? (rely + ygridsize ) : maxy ;
492+ return decodeExtension (decodeLimits , cornery , cornerx , dividerx << 2 , dividery , extrapostfix ,
493+ 0 , decodeMaxy , decodeMaxx ); // grid
346494 }
347495
348496 @ Nonnull
@@ -417,6 +565,7 @@ private static Point decodeNameless(final String str, final int firstrec, final
417565 int side = DataAccess .smartDiv (m );
418566 int xSIDE = side ;
419567
568+ final int maxx = Data .getBoundaries (m ).getMaxX ();
420569 final int maxy = Data .getBoundaries (m ).getMaxY ();
421570 final int minx = Data .getBoundaries (m ).getMinX ();
422571 final int miny = Data .getBoundaries (m ).getMinY ();
@@ -439,6 +588,7 @@ private static Point decodeNameless(final String str, final int firstrec, final
439588
440589 if (dx >= xSIDE ) // else out-of-range!
441590 {
591+ LOG .error ("*** decodeNameless({}) : dx {} > xSIDE {}" , str , dx , xSIDE );
442592 return Point .undefined (); // return undefined (out of range!)
443593 }
444594
@@ -447,7 +597,8 @@ private static Point decodeNameless(final String str, final int firstrec, final
447597
448598 final int cornerx = minx + ((dx * dividerx4 ) / 4 );
449599 final int cornery = maxy - (dy * dividery );
450- return decodeExtension (cornery , cornerx , dividerx4 , -dividery , extrapostfix , ((dx * dividerx4 ) % 4 ) ); // nameless
600+ return decodeExtension (null , cornery , cornerx , dividerx4 , -dividery , extrapostfix ,
601+ ((dx * dividerx4 ) % 4 ), miny , maxx ); // nameless
451602 }
452603
453604 @ Nonnull
@@ -465,6 +616,7 @@ private static Point decodeAutoHeader(final String input, final int m, final Str
465616 i = m ;
466617 while (true ) {
467618 if ((Data .recType (i )<2 ) || (Data .calcCodex (i ) != codexm )) {
619+ LOG .error ("*** decodeAutoHeader({}) : out of {} records" , input , codexm );
468620 return Point .undefined (); // return undefined
469621 }
470622
@@ -503,10 +655,12 @@ private static Point decodeAutoHeader(final String input, final int m, final Str
503655 final int cornerx = minx + (vx * dividerx );
504656
505657 if (cornerx < minx || cornerx >= maxx || cornery < miny || cornery > maxy ) {
658+ LOG .error ("*** decodeAutoHeader({}) : corner {}, {} out of bounds" , input , cornery , cornerx );
506659 return Point .undefined (); // corner out of bounds
507660 }
508-
509- return decodeExtension (cornery , cornerx , dividerx << 2 , -dividery , extrapostfix , 0 ); // autoheader
661+
662+ return decodeExtension (null , cornery , cornerx , dividerx << 2 , -dividery , extrapostfix ,
663+ 0 , miny , maxx ); // autoheader
510664 }
511665 storageStart += product ;
512666 i ++;
@@ -717,7 +871,9 @@ private static int decodeBase31(final String code) {
717871 }
718872
719873 @ Nonnull
720- private static Point decodeExtension (final int y , final int x , final int dividerx4 , final int dividery0 , final String extrapostfix , final int lon_offset4 ) {
874+ private static Point decodeExtension (@ Nullable DecodeLimits decodeLimits , final int y , final int x , final int dividerx4 , final int dividery0 ,
875+ final String extrapostfix , final int lon_offset4 , final int extremeLat32 , final int maxLon32 ) {
876+ if (decodeLimits ==null ) decodeLimits = new DecodeLimits ();
721877 final double dividerx = dividerx4 / 4.0 , dividery = (double ) dividery0 ;
722878 double processor = 1 ;
723879 int lon32 = 0 ;
@@ -730,6 +886,7 @@ private static Point decodeExtension(final int y, final int x, final int divider
730886 int c1 = (int ) extrapostfix .charAt (idx ++);
731887 c1 = DECODE_CHARS [c1 ];
732888 if (c1 < 0 || c1 == 30 ) {
889+ LOG .error ("*** decodeExtension({}) : illegal c1 {}" , extrapostfix , c1 );
733890 return Point .undefined ();
734891 }
735892 final int y1 = c1 / 5 ;
@@ -740,6 +897,7 @@ private static Point decodeExtension(final int y, final int x, final int divider
740897 int c2 = (int ) extrapostfix .charAt (idx ++);
741898 c2 = DECODE_CHARS [c2 ];
742899 if (c2 < 0 || c2 == 30 ) {
900+ LOG .error ("*** decodeExtension({}) : illegal c2 {}" , extrapostfix , c2 );
743901 return Point .undefined ();
744902 }
745903 y2 = c2 / 6 ;
@@ -758,24 +916,32 @@ private static Point decodeExtension(final int y, final int x, final int divider
758916 double lat = y + ((lat32 * dividery ) / processor );
759917 double lon = x + ((lon32 * dividerx ) / processor ) + ( lon_offset4 / 4.0 );
760918
761- /* FORCE_RECODE : TO DO!
762- var range = {minlon:lon, maxlon:lon, minlat:lat, maxlat:lat};
763- if (odd) {
764- range.maxlon += (dividerx / (processor / 6));
765- range.maxlat += (dividery / (processor / 5));
766- } else {
767- range.maxlon += (dividerx / processor);
768- range.maxlat += (dividery / processor);
769- } // FORCE_RECODE */
770-
771- if (odd ) {
772- lon += (dividerx / (2 * (processor / 6 )));
773- lat += (dividery / (2 * (processor / 5 )));
774- } else {
775- lon += (dividerx / (2 * processor ));
776- lat += (dividery / (2 * processor ));
919+ // determine the range of coordinates that are encode to this mapcode
920+ if (odd ) { // odd
921+ decodeLimits .setFromDelta (lat , lon , (dividery / (processor / 5 )), (dividerx / (processor / 6 )) );
922+ } else { // not odd
923+ decodeLimits .setFromDelta (lat , lon , (dividery / processor ), (dividerx / processor ) );
777924 } // not odd
778925
779- return Point .fromDeg ( lat / 1000000.0 , lon / 1000000.0 );
926+ // FORCE_RECODE - restrict the coordinate range to the extremes that were provided
927+ if (decodeLimits .cutMaxX (maxLon32 )==false ) {
928+ LOG .error ("*** decodeExtension({}) : cutMaxX {}" , extrapostfix , maxLon32 );
929+ return Point .undefined ();
930+ }
931+ if (dividery >= 0 )
932+ {
933+ if (decodeLimits .cutMaxY (extremeLat32 )==false ) {
934+ LOG .error ("*** decodeExtension({}) : cutMaxY {}" , extrapostfix , extremeLat32 );
935+ return Point .undefined ();
936+ }
937+ } else { // dividery < 0
938+ if (decodeLimits .cutMinY (extremeLat32 )==false ) {
939+ LOG .error ("*** decodeExtension({}) : cutMinY {}" , extrapostfix , extremeLat32 );
940+ return Point .undefined ();
941+ }
942+ }
943+
944+ // return a coordinate exactly in the middle of the range
945+ return Point .fromDeg ( (decodeLimits .getMinY () + decodeLimits .getMaxY ()) / 2000000.0 , (decodeLimits .getMinX () + decodeLimits .getMaxX ()) / 2000000.0 );
780946 }
781947}
0 commit comments