2222import javax .annotation .Nonnull ;
2323import javax .annotation .Nullable ;
2424
25+ // simple class to represent all the coordinates that would deliver a particular mapcode
2526class DecodeLimits {
26-
27- // construct empty
28- public void DecodeLimits () {
29- defined = false ;
30- }
27+ public Point min ;
28+ public Point max ;
3129
3230 // 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- }
31+ public void setFromFractions (final double y , final double x , final double yDelta , final double xDelta ) {
32+ assert (xDelta >= 0 );
4233 if (yDelta < 0 ) {
43- maxy = y ;
44- miny = y + yDelta ;
34+ min = Point . fromFractionDeg ( y + yDelta , x ) ;
35+ max = Point . fromFractionDeg ( y , x + xDelta ) ;
4536 }
4637 else {
47- miny = y ;
48- maxy = y + yDelta ;
38+ min = Point . fromFractionDeg ( y , x ) ;
39+ max = Point . fromFractionDeg ( y + yDelta , x + xDelta ) ;
4940 }
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 );
8141 }
8242
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 );
43+ @ Nonnull
44+ public Point midPoint () {
45+ return min .getMidPoint (max );
9946 }
10047
10148 @ Nonnull
10249 @ Override
10350 public String toString () {
104- return defined ? ("(" + miny + ", " + minx + " )-(" + maxy + ", " + maxx + " )" ) : "undefined" ;
51+ return ("(" + min + ")-(" + max + ")" );
10552 }
106-
107- // private parts
108- private boolean defined ;
109- private double miny ;
110- private double maxy ;
111- private double minx ;
112- private double maxx ;
11353}
11454
11555class Decoder {
@@ -202,7 +142,7 @@ static Point decode(@Nonnull final String argMapcode,
202142 Data .getBoundaries (i ).getMinX (), Data .getBoundaries (i ).getMinY (),
203143 Data .getBoundaries (i ).getMaxX (), Data .getBoundaries (i ).getMaxY (),
204144 i , extrapostfix , decodeLimits );
205-
145+
206146 if (Data .isRestricted (i ) && result .isDefined ()) {
207147 boolean fitssomewhere = false ;
208148 int j ;
@@ -222,24 +162,28 @@ static Point decode(@Nonnull final String argMapcode,
222162 if (!Data .isRestricted (j )) {
223163 int minx = Data .getBoundaries (j ).getMinX ();
224164 int maxx = Data .getBoundaries (j ).getMaxX ();
225- if ((maxx < 0 ) && (result .getLonDeg () > 0 )) {
165+ if ((maxx < 0 ) && (result .getLonMicroDeg () > 1 )) {
226166 minx += 360000000 ;
227167 maxx += 360000000 ;
228168 }
169+ else if ((maxx > 1 ) && (result .getLonMicroDeg () < 0 )) {
170+ minx -= 360000000 ;
171+ maxx -= 360000000 ;
172+ }
229173 final int miny = Data .getBoundaries (j ).getMinY ();
230174 final int maxy = Data .getBoundaries (j ).getMaxY ();
231175
232176 // force pt within decodeLimits
233177 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-
178+ if (miny * Point . MICROLAT_TO_FRACTIONS_FACTOR < decodeLimits .max . getLatFractions ()) { pt .setMinLatToMicroDeg (miny ); }
179+ if (maxy * Point . MICROLAT_TO_FRACTIONS_FACTOR > decodeLimits .min . getLatFractions ()) { pt .setMaxLatToMicroDeg (maxy ); }
180+ if (minx * Point . MICROLON_TO_FRACTIONS_FACTOR < decodeLimits .max . getLonFractions ()) { pt .setMinLonToMicroDeg (minx ); }
181+ if (maxx * Point . MICROLON_TO_FRACTIONS_FACTOR > decodeLimits .min . getLonFractions ()) { pt .setMaxLonToMicroDeg (maxx ); }
182+
239183 // better?
240- if ( pt .getLatDeg ()* 1000000 > decodeLimits .getMinY () && pt .getLatDeg ()* 1000000 < decodeLimits .getMaxY () &&
241- pt .getLonDeg ()* 1000000 > decodeLimits .getMinX () && pt .getLonDeg ()* 1000000 < decodeLimits .getMaxX () &&
242- Data .getBoundaries (j ).containsPoint (pt ))
184+ if ( pt .getLatFractions () >= decodeLimits .min . getLatFractions () && pt .getLatFractions () < decodeLimits .max . getLatFractions () &&
185+ pt .getLonFractions () >= decodeLimits .min . getLonFractions () && pt .getLonFractions () < decodeLimits .max . getLonFractions () &&
186+ Data .getBoundaries (j ).containsPoint (pt ))
243187 {
244188 result = Point .fromPoint (pt );
245189 fitssomewhere = true ;
@@ -250,6 +194,7 @@ static Point decode(@Nonnull final String argMapcode,
250194 } //FORCE_RECODE
251195
252196 if (!fitssomewhere ) {
197+ LOG .info ("FAILED {}-{} decode {} fits no smaller rectangle" ,mapcode ,extrapostfix ,result );
253198 result .setUndefined ();
254199 }
255200 }
@@ -277,11 +222,8 @@ static Point decode(@Nonnull final String argMapcode,
277222 }
278223
279224 if (result .isDefined ()) {
280- if (result .getLonMicroDeg () > 180000000 ) {
281- result = Point .fromMicroDeg (result .getLatMicroDeg (), result .getLonMicroDeg () - 360000000 );
282- } else if (result .getLonMicroDeg () < -180000000 ) {
283- result = Point .fromMicroDeg (result .getLatMicroDeg (), result .getLonMicroDeg () + 360000000 );
284- }
225+
226+ result .wrap ();
285227
286228 // LIMIT_TO_OUTRECT : make sure it fits the country
287229 if (ccode != CCODE_EARTH ) {
@@ -291,6 +233,7 @@ static Point decode(@Nonnull final String argMapcode,
291233 final int xdiv8 = Common .xDivider (miny , maxy ) / 4 ;
292234 // should be /8 but there's some extra margin
293235 if (!mapcoderRect .extendBounds (xdiv8 , 60 ).containsPoint (result )) {
236+ LOG .info ("FAILED {}-{}: result {} not in encompassing {}" ,mapcode ,extrapostfix ,result ,mapcoderRect );
294237 result .setUndefined (); // decodes outside the official territory
295238 // limit
296239 }
@@ -406,7 +349,7 @@ public Unicode2Ascii(final char min, final char max, @Nonnull final String conve
406349
407350 @ Nonnull
408351 private static Point decodeGrid (final String str , final int minx , final int miny , final int maxx , final int maxy ,
409- final int m , final String extrapostfix , DecodeLimits decodeLimits ) {
352+ final int m , final String extrapostfix , @ Nullable DecodeLimits decodeLimits ) {
410353 // for a well-formed result, and integer variables
411354 String result = str ;
412355 int relx ;
@@ -483,7 +426,7 @@ private static Point decodeGrid(final String str, final int minx, final int miny
483426
484427 Point pt = Point .fromMicroDeg (cornery ,cornerx );
485428 if (!(Data .getBoundaries (m ).containsPoint (pt ))) {
486- LOG .info ("*** decodeGrid({}) : {} not in {}" , str , pt , Data .getBoundaries (m ));
429+ LOG .info ("FAILED decodeGrid({}) : {} not in {}" , str , pt , Data .getBoundaries (m ));
487430 return Point .undefined (); // already out of range
488431 }
489432
@@ -588,7 +531,7 @@ private static Point decodeNameless(final String str, final int firstrec, final
588531
589532 if (dx >= xSIDE ) // else out-of-range!
590533 {
591- LOG .error ("*** decodeNameless({}) : dx {} > xSIDE {}" , str , dx , xSIDE );
534+ LOG .error ("FAILED decodeNameless({}) : dx {} > xSIDE {}" , str , dx , xSIDE );
592535 return Point .undefined (); // return undefined (out of range!)
593536 }
594537
@@ -616,7 +559,7 @@ private static Point decodeAutoHeader(final String input, final int m, final Str
616559 i = m ;
617560 while (true ) {
618561 if ((Data .recType (i )<2 ) || (Data .calcCodex (i ) != codexm )) {
619- LOG .error ("*** decodeAutoHeader({}) : out of {} records" , input , codexm );
562+ LOG .error ("FAILED decodeAutoHeader({}) : out of {} records" , input , codexm );
620563 return Point .undefined (); // return undefined
621564 }
622565
@@ -655,7 +598,7 @@ private static Point decodeAutoHeader(final String input, final int m, final Str
655598 final int cornerx = minx + (vx * dividerx );
656599
657600 if (cornerx < minx || cornerx >= maxx || cornery < miny || cornery > maxy ) {
658- LOG .error ("*** decodeAutoHeader({}) : corner {}, {} out of bounds" , input , cornery , cornerx );
601+ LOG .error ("FAILED decodeAutoHeader({}) : corner {}, {} out of bounds" , input , cornery , cornerx );
659602 return Point .undefined (); // corner out of bounds
660603 }
661604
@@ -871,10 +814,10 @@ private static int decodeBase31(final String code) {
871814 }
872815
873816 @ Nonnull
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 ();
877- final double dividerx = dividerx4 / 4.0 , dividery = (double ) dividery0 ;
817+ private static Point decodeExtension (@ Nullable DecodeLimits decodeLimits , final int y , final int x , final int dividerx0 , final int dividery0 ,
818+ final String extrapostfix , final int lon_offset4 , final int extremeLatMicroDeg , final int maxLonMicroDeg ) {
819+ if (decodeLimits == null ) { decodeLimits = new DecodeLimits (); }
820+ double dividerx4 = ( double ) dividerx0 , dividery = (double ) dividery0 ;
878821 double processor = 1 ;
879822 int lon32 = 0 ;
880823 int lat32 = 0 ;
@@ -886,7 +829,7 @@ private static Point decodeExtension(@Nullable DecodeLimits decodeLimits, final
886829 int c1 = (int ) extrapostfix .charAt (idx ++);
887830 c1 = DECODE_CHARS [c1 ];
888831 if (c1 < 0 || c1 == 30 ) {
889- LOG .error ("*** decodeExtension({}) : illegal c1 {}" , extrapostfix , c1 );
832+ LOG .error ("FAILED decodeExtension({}) : illegal c1 {}" , extrapostfix , c1 );
890833 return Point .undefined ();
891834 }
892835 final int y1 = c1 / 5 ;
@@ -897,7 +840,7 @@ private static Point decodeExtension(@Nullable DecodeLimits decodeLimits, final
897840 int c2 = (int ) extrapostfix .charAt (idx ++);
898841 c2 = DECODE_CHARS [c2 ];
899842 if (c2 < 0 || c2 == 30 ) {
900- LOG .error ("*** decodeExtension({}) : illegal c2 {}" , extrapostfix , c2 );
843+ LOG .error ("FAILED decodeExtension({}) : illegal c2 {}" , extrapostfix , c2 );
901844 return Point .undefined ();
902845 }
903846 y2 = c2 / 6 ;
@@ -913,35 +856,50 @@ private static Point decodeExtension(@Nullable DecodeLimits decodeLimits, final
913856 lat32 = lat32 * 30 + (y1 * 5 ) + y2 ;
914857 }
915858
916- double lat = y + ((lat32 * dividery ) / processor );
917- double lon = x + ((lon32 * dividerx ) / processor ) + ( lon_offset4 / 4.0 );
859+ while (processor < Point .MAX_PRECISION_FACTOR ) {
860+ dividerx4 *= 30 ;
861+ dividery *= 30 ;
862+ processor *= 30 ;
863+ }
864+
865+ double lon4 = (x * 4 * Point .MAX_PRECISION_FACTOR ) + ((lon32 * dividerx4 )) + (lon_offset4 * Point .MAX_PRECISION_FACTOR );
866+ double lat1 = (y * Point .MAX_PRECISION_FACTOR ) + ((lat32 * dividery ));
918867
919868 // determine the range of coordinates that are encode to this mapcode
920869 if (odd ) { // odd
921- decodeLimits .setFromDelta ( lat , lon , ( dividery / ( processor / 5 )), ( dividerx / ( processor / 6 )) );
870+ decodeLimits .setFromFractions ( lat1 , lon4 , 5 * dividery , 6 * dividerx4 );
922871 } else { // not odd
923- decodeLimits .setFromDelta ( lat , lon , ( dividery / processor ), ( dividerx / processor ) );
872+ decodeLimits .setFromFractions ( lat1 , lon4 , dividery , dividerx4 );
924873 } // not odd
925874
926875 // 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 );
876+ if (decodeLimits .max .getLonFractions () > (maxLonMicroDeg * Point .MICROLON_TO_FRACTIONS_FACTOR )) {
877+ decodeLimits .max .setLonMicroDeg (maxLonMicroDeg );
878+ if (decodeLimits .max .getLonFractions () <= decodeLimits .min .getLonFractions ()) {
879+ LOG .error ("FAILED decodeExtension({}) : cutMaxX {}" , extrapostfix , maxLonMicroDeg );
935880 return Point .undefined ();
936881 }
882+ }
883+
884+ if (dividery >= 0 ) {
885+ if (decodeLimits .max .getLatFractions () > (extremeLatMicroDeg * Point .MICROLAT_TO_FRACTIONS_FACTOR )) {
886+ decodeLimits .max .setLatMicroDeg (extremeLatMicroDeg );
887+ if (decodeLimits .max .getLatFractions () <= decodeLimits .min .getLatFractions ()) {
888+ LOG .error ("FAILED decodeExtension({}) : cutMaxY {}" , extrapostfix , extremeLatMicroDeg );
889+ return Point .undefined ();
890+ }
891+ }
937892 } else { // dividery < 0
938- if (decodeLimits .cutMinY (extremeLat32 )==false ) {
939- LOG .error ("*** decodeExtension({}) : cutMinY {}" , extrapostfix , extremeLat32 );
940- return Point .undefined ();
893+ if (decodeLimits .min .getLatFractions () < (extremeLatMicroDeg * Point .MICROLAT_TO_FRACTIONS_FACTOR )) {
894+ decodeLimits .min .setLatMicroDeg (extremeLatMicroDeg );
895+ if (decodeLimits .max .getLatFractions () < decodeLimits .min .getLatFractions ()) {
896+ LOG .error ("FAILED decodeExtension({}) : cutMinY {}" , extrapostfix , extremeLatMicroDeg );
897+ return Point .undefined ();
898+ }
941899 }
942900 }
943901
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 );
902+ // return the coordinate in the center of the mapcode-defined zone
903+ return decodeLimits .midPoint ( );
946904 }
947905}
0 commit comments