# HG changeset patch # User jgindin@chagall # Date 1644203500 18000 # Sun Feb 06 22:11:40 2022 -0500 # Node ID 8f46fef64c21ed8c259a254316a7446d2abdbccd # Parent 81aa109a82877d30a26c6fc2d71859174e09a0e2 Refactor LatitudeLongitudeParser to be simpler and easier to move to kotlin. diff --git a/Android/src/main/java/com/gindin/zmanim/android/ZmanimActivity.java b/Android/src/main/java/com/gindin/zmanim/android/ZmanimActivity.java --- a/Android/src/main/java/com/gindin/zmanim/android/ZmanimActivity.java +++ b/Android/src/main/java/com/gindin/zmanim/android/ZmanimActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021. Jay R. Gindin + * Copyright (c) 2022. Jay R. Gindin */ package com.gindin.zmanim.android; @@ -517,10 +517,12 @@ // The user didn't grant our permissions. The app can work, but they'll have to tell us where we are. if ( ( lastKnownLocation != null ) && !lastKnownLocation.isInvalid ) { - LatitudeLongitudeParser.Holder holder = LatitudeLongitudeParser.parse( Double.toString( lastKnownLocation.getLatitude() ), "", "", LatitudeLongitudeParser.Direction.NORTH ); + LatitudeLongitudeParser holder = LatitudeLongitudeParser.latitude( + LatitudeLongitudeParser.Direction.NORTH, Double.toString( lastKnownLocation.getLatitude() ), "", "" ); String latitude = holder.toFullString( false ); - holder = LatitudeLongitudeParser.parse( Double.toString( lastKnownLocation.getLongitude() ), "", "", LatitudeLongitudeParser.Direction.EAST ); + holder = LatitudeLongitudeParser.longitude( + LatitudeLongitudeParser.Direction.EAST, Double.toString( lastKnownLocation.getLongitude() ), "", "" ); String longitude = holder.toFullString( false ); locationManagementPrefs diff --git a/Android/src/main/java/com/gindin/zmanim/android/location/acquirers/LatitudeLongitudeLocationAcquirer.java b/Android/src/main/java/com/gindin/zmanim/android/location/acquirers/LatitudeLongitudeLocationAcquirer.java --- a/Android/src/main/java/com/gindin/zmanim/android/location/acquirers/LatitudeLongitudeLocationAcquirer.java +++ b/Android/src/main/java/com/gindin/zmanim/android/location/acquirers/LatitudeLongitudeLocationAcquirer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021. Jay R. Gindin + * Copyright (c) 2022. Jay R. Gindin */ package com.gindin.zmanim.android.location.acquirers; @@ -68,7 +68,7 @@ } try { - ourLocation.atLatitude( LatitudeLongitudeParser.LATITUDE_PARSER.parseAsDouble( latitudePref ) ); + ourLocation.atLatitude( LatitudeLongitudeParser.latitudeValue( latitudePref ) ); return null; } @@ -90,7 +90,7 @@ } try { - ourLocation.atLongitude( LatitudeLongitudeParser.LONGITUDE_PARSER.parseAsDouble( longitudePref ) ); + ourLocation.atLongitude( LatitudeLongitudeParser.longitudeValue( longitudePref ) ); return null; } diff --git a/Android/src/main/java/com/gindin/zmanim/android/prefs/LongitudeLatitudePreference.java b/Android/src/main/java/com/gindin/zmanim/android/prefs/LongitudeLatitudePreference.java --- a/Android/src/main/java/com/gindin/zmanim/android/prefs/LongitudeLatitudePreference.java +++ b/Android/src/main/java/com/gindin/zmanim/android/prefs/LongitudeLatitudePreference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014. Jay R. Gindin + * Copyright (c) 2022. Jay R. Gindin */ package com.gindin.zmanim.android.prefs; @@ -33,7 +33,7 @@ private View view; private boolean showingLatitude; - private LatitudeLongitudeParser.Holder valueHolder; + private LatitudeLongitudeParser valueHolder; public LongitudeLatitudePreference( @@ -51,10 +51,10 @@ this.showingLatitude = showingLatitude; if ( showingLatitude ) { - valueHolder = LatitudeLongitudeParser.LATITUDE_PARSER.parse( value ); + valueHolder = LatitudeLongitudeParser.latitude( value ); } else { - valueHolder = LatitudeLongitudeParser.LONGITUDE_PARSER.parse( value ); + valueHolder = LatitudeLongitudeParser.longitude( value ); } } @@ -84,16 +84,16 @@ ValueFilter minutesFilter = new ValueFilter( 0, 60 ); - EditText degrees = (EditText)view.findViewById( R.id.lat_long_editor_degrees_editor ); - degrees.setText( String.format( "%d", valueHolder.getDegrees() ) ); + EditText degrees = view.findViewById( R.id.lat_long_editor_degrees_editor ); + degrees.setText( String.format( "%d", valueHolder.degrees ) ); degrees.setFilters( new InputFilter[] { degreesFilter } ); - EditText minutes = (EditText)view.findViewById( R.id.lat_long_editor_minutes_editor ); - minutes.setText( String.format( "%d", valueHolder.getMinutes() ) ); + EditText minutes = view.findViewById( R.id.lat_long_editor_minutes_editor ); + minutes.setText( String.format( "%d", valueHolder.minutes ) ); minutes.setFilters( new InputFilter[] { minutesFilter } ); - TextView seconds = (TextView)view.findViewById( R.id.lat_long_editor_seconds_editor ); - seconds.setText( String.format( "%f", valueHolder.getSeconds() ) ); + TextView seconds = view.findViewById( R.id.lat_long_editor_seconds_editor ); + seconds.setText( String.format( "%f", valueHolder.seconds ) ); // Configuring the spinner comes straight from the Android JavaDocs: @@ -112,13 +112,13 @@ ArrayAdapter adapter = ArrayAdapter.createFromResource( getContext(), arrayResource, android.R.layout.simple_spinner_item ); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - Spinner spinner = (Spinner)view.findViewById( R.id.lat_long_editor_ns_ew_picker ); + Spinner spinner = view.findViewById( R.id.lat_long_editor_ns_ew_picker ); spinner.setAdapter( adapter ); - TextView label = (TextView)view.findViewById( R.id.lat_long_editor_ns_text ); + TextView label = view.findViewById( R.id.lat_long_editor_ns_text ); label.setText( labelText ); - LatitudeLongitudeParser.Direction direction = valueHolder.getDirection(); + LatitudeLongitudeParser.Direction direction = valueHolder.direction; if ( ( LatitudeLongitudeParser.Direction.NORTH == direction ) || ( LatitudeLongitudeParser.Direction.EAST == direction ) ) { spinner.setSelection( 0, true ); } @@ -137,27 +137,29 @@ } // Have to convert the degrees, minutes, N/S (or E/W) to something we can store... - TextView value = (TextView)view.findViewById( R.id.lat_long_editor_degrees_editor ); + TextView value = view.findViewById( R.id.lat_long_editor_degrees_editor ); String degreesText = value.getText().toString(); - value = (TextView)view.findViewById( R.id.lat_long_editor_minutes_editor ); + value = view.findViewById( R.id.lat_long_editor_minutes_editor ); String minutesText = value.getText().toString(); - value = (TextView)view.findViewById( R.id.lat_long_editor_seconds_editor ); + value = view.findViewById( R.id.lat_long_editor_seconds_editor ); String secondsText = value.getText().toString(); LatitudeLongitudeParser.Direction direction; - Spinner spinner = (Spinner)view.findViewById( R.id.lat_long_editor_ns_ew_picker ); + Spinner spinner = view.findViewById( R.id.lat_long_editor_ns_ew_picker ); int item = spinner.getSelectedItemPosition(); + LatitudeLongitudeParser holder; if ( showingLatitude ) { direction = ( 0 == item ? LatitudeLongitudeParser.Direction.NORTH : LatitudeLongitudeParser.Direction.SOUTH ); + holder = LatitudeLongitudeParser.latitude( direction, degreesText, minutesText, secondsText ); } else { direction = ( 0 == item ? LatitudeLongitudeParser.Direction.EAST : LatitudeLongitudeParser.Direction.WEST ); + holder = LatitudeLongitudeParser.longitude( direction, degreesText, minutesText, secondsText ); } - LatitudeLongitudeParser.Holder holder = LatitudeLongitudeParser.parse( degreesText, minutesText, secondsText, direction ); String newValue = holder.toFullString( false ); if ( callChangeListener( newValue ) ) { diff --git a/ZmanLib/src/main/java/com/gindin/zmanlib/location/LatitudeLongitudeParser.java b/ZmanLib/src/main/java/com/gindin/zmanlib/location/LatitudeLongitudeParser.java --- a/ZmanLib/src/main/java/com/gindin/zmanlib/location/LatitudeLongitudeParser.java +++ b/ZmanLib/src/main/java/com/gindin/zmanlib/location/LatitudeLongitudeParser.java @@ -1,221 +1,205 @@ /* - * Copyright (c) 2014. Jay R. Gindin + * Copyright (c) 2022. Jay R. Gindin */ package com.gindin.zmanlib.location; +import com.gindin.util.Pair; + /** * Used to parse a string into a latitude or a longitude. - *

- * + *

+ *

* Supported formats are: - *

+ * */ -public abstract class LatitudeLongitudeParser { +public class LatitudeLongitudeParser { + + private static final int MAX_LATITUDE = 90; - public static enum Direction { + private static final int MAX_LONGITUDE = 180; + + public enum Direction { NORTH( "N" ), SOUTH( "S" ), EAST( "E" ), WEST( "W" ); - private final String abbreviation; + private final String abbreviation; + Direction( String abbreviation ) { this.abbreviation = abbreviation; } } - public static class Holder { + private static final int MINUTES_PER_DEGREE = 60; + private static final int SECONDS_PER_MINUTE = 60; - private Direction direction; - private int minutes; - private int degrees; - private double seconds = 0; - - - public Holder() { - this( Direction.NORTH, 0, 0, 0 ); - } + public final Direction direction; + public final int minutes; + public final int degrees; + public final double seconds; - public Holder( - Direction direction, - int degrees, - int minutes, - double seconds - ) { - setDirection( direction ); - setDegrees( degrees ); - setMinutes( minutes ); - setSeconds( seconds ); + @SuppressWarnings( "AssignmentToMethodParameter" ) + protected LatitudeLongitudeParser( + Direction direction, + int degrees, + int minutes, + double seconds, + int maxDegrees + ) { + + if ( degrees < 0 ) { + if ( Direction.NORTH == direction ) { + direction = Direction.SOUTH; + } + else if ( Direction.EAST == direction ) { + direction = Direction.WEST; + } + + degrees = degrees * -1; } + this.direction = direction; + this.minutes = minutes; + this.seconds = seconds; + + // If the value is still too large, then just choose zero. + if ( degrees > maxDegrees ) { + this.degrees = 0; + } + else { + this.degrees = degrees; + } + } + - public Direction getDirection() { - return direction; + public String toFullString( boolean withMarkers ) { + return direction.abbreviation + " " + formattedDMS( withMarkers ); + } + + + public String toSimpleString( boolean withMarkers ) { + String str; + if ( ( Direction.SOUTH == direction ) || ( Direction.WEST == direction ) ) { + str = direction.abbreviation + " "; } - - void setDirection( Direction direction ) { - this.direction = direction; + else { + str = ""; } - void updateDirection() { + return str + formattedDMS( withMarkers ); + } + + + private String formattedDMS( boolean withMarkers ) { - if ( getDegrees() < 1 ) { - if ( Direction.NORTH == getDirection() ) { - setDirection( Direction.SOUTH ); - } - else if ( Direction.EAST == getDirection() ) { - setDirection( Direction.WEST ); - } + StringBuilder str = new StringBuilder().append( degrees ); + if ( ( 0 != minutes ) || ( 0 != seconds ) ) { + str.append( " " ).append( minutes ); + if ( withMarkers ) { + str.append( "'" ); + } + } - setDegrees( getDegrees() * -1 ); + if ( 0 != seconds ) { + str.append( " " ).append( seconds ); + if ( withMarkers ) { + str.append( "\"" ); } } - - public int getDegrees() { - return degrees; - } - - void setDegrees( int degrees ) { - this.degrees = degrees; - } - - - public int getMinutes() { - return minutes; - } - - void setMinutes( int minutes ) { - this.minutes = minutes; - } - + return str.toString(); + } - public double getSeconds() { - return seconds; - } - - void setSeconds( double seconds ) { - this.seconds = seconds; - } - - - public String toFullString( boolean withMarkers ) { - return direction.abbreviation + " " + formattedDMS( withMarkers ); - } - - - public String toSimpleString( boolean withMarkers ) { - String str; - if ( ( Direction.SOUTH == direction ) || ( Direction.WEST == direction ) ) { - str = direction.abbreviation + " "; - } - else { - str = ""; - } - - return str + formattedDMS( withMarkers ); - } + static public LatitudeLongitudeParser latitude( + Direction direction, + int degrees, + int minutes, + int seconds + ) { + return new LatitudeLongitudeParser( direction, degrees, minutes, seconds, MAX_LATITUDE ); + } - private String formattedDMS( boolean withMarkers ) { - - StringBuilder str = new StringBuilder().append( degrees ); - if ( ( 0 != minutes ) || ( 0 != seconds ) ) { - str.append( " " ).append( minutes ); - if ( withMarkers ) { - str.append( "'" ); - } - } - - if ( 0 != seconds ) { - str.append( " " ).append( seconds ); - if ( withMarkers ) { - str.append( "\"" ); - } - } - - return str.toString(); - } + static public LatitudeLongitudeParser latitude( + Direction direction, + String degreesText, + String minutesText, + String secondsText + ) { + return parse( direction, degreesText, minutesText, secondsText, MAX_LATITUDE ); } - public static final LatitudeLongitudeParser LATITUDE_PARSER = new LatitudeParser(); - public static final LatitudeLongitudeParser LONGITUDE_PARSER = new LongitudeParser(); - - protected abstract double getMax(); + static public LatitudeLongitudeParser latitude( String value ) { + return parse( value, Direction.NORTH, MAX_LATITUDE ); + } - @SuppressWarnings( "AssignmentToMethodParameter" ) - public Holder parse( String value ) { - - Holder holder = new Holder(); - - // Make sure there's no extra whitespace... - value = value.trim(); + static public double latitudeValue( String value ) { + + double latitude = parseAsDouble( value, Direction.NORTH, MAX_LATITUDE ); - // NOTE: I used to try to parse the value directly into a double. I don't think this really makes sense, though, - // and quite possibly explains why so many users got confused and couldn't make this functionality work the - // way they thought it should. -// try { -// return Double.parseDouble( value ); -// } -// catch ( NumberFormatException e ) { -// // Well, keep trying to parse. -// } -// - value = extractDirection( value, holder ); - - // Clean up the input.... - value = value.replace( '.', ' ' ); - value = value.replace( '\'', ' ' ); - value = value.replace( '\"', ' ' ); - value = value.trim(); - - - String[] latitudeComponents = value.split( " " ); - if ( 0 == latitudeComponents.length ) { - throw new IllegalArgumentException( "Can't parse value: " + value ); + // Latitude must be -90 <= lat <= 90 + if ( ( latitude < -MAX_LATITUDE ) || ( latitude > MAX_LATITUDE ) ) { + latitude = 0; } - holder.setDegrees( Integer.parseInt( latitudeComponents[ 0 ].trim() ) ); - holder.updateDirection(); - - if ( latitudeComponents.length > 1 ) { - holder.setMinutes( Integer.parseInt( latitudeComponents[ 1 ].trim() ) ); - } - - if ( latitudeComponents.length > 2 ) { - holder.setSeconds( Integer.parseInt( latitudeComponents[ 2 ].trim() ) ); - } - - // Ahhh, but wait. IFF the value is too large, then perhaps what the user was entering was, after all, in - // decimal format? - // NOTE: Removed this for the same reason as why I removed the blanket parsing into a double... -// if ( holder.getDegrees() > getMax() && ( holder.getMinutes() > 0 ) ) { -// holder.setDegrees( Double.parseDouble( latitudeComponents[0] + "." + latitudeComponents[1] ) ); -// } - - // If the value is still too large, then just choose zero. - if ( holder.getDegrees() > getMax() ) { - holder.setDegrees( 0 ); - } - - return holder; + return latitude; } - public static Holder parse( - String degreesText, - String minutesText, - String secondsText, - LatitudeLongitudeParser.Direction direction + static public LatitudeLongitudeParser longitude( + Direction direction, + int degrees, + int minutes, + int seconds + ) { + return new LatitudeLongitudeParser( direction, degrees, minutes, seconds, MAX_LONGITUDE ); + } + + + static public LatitudeLongitudeParser longitude( + Direction direction, + String degreesText, + String minutesText, + String secondsText + ) { + return parse( direction, degreesText, minutesText, secondsText, MAX_LONGITUDE ); + } + + + static public LatitudeLongitudeParser longitude( String value ) { + return parse( value, Direction.EAST, MAX_LONGITUDE ); + } + + + static public double longitudeValue( String value ) { + + double longitude = parseAsDouble( value, Direction.EAST, MAX_LONGITUDE ); + + // Longitude must be -180 <= long <= 180 + if ( ( longitude < -MAX_LONGITUDE ) || ( longitude > MAX_LONGITUDE ) ) { + longitude = MAX_LONGITUDE; + } + + return longitude; + } + + + private static LatitudeLongitudeParser parse( + Direction direction, + String degreesText, + String minutesText, + String secondsText, + int maxDegrees ) { boolean parseMinutes = true; @@ -223,7 +207,7 @@ int degrees; int minutes = 0; double seconds = 0; - + try { degrees = Integer.parseInt( degreesText ); } @@ -236,10 +220,10 @@ degrees = (int)degreesDouble; // Now, convert the decimal portion to the minutes. http://geography.about.com/library/howto/htdegrees.htm - double doubleMinutes = Math.abs( ( ( degreesDouble - degrees ) * 60 ) ); + double doubleMinutes = Math.abs( ( ( degreesDouble - degrees ) * MINUTES_PER_DEGREE ) ); minutes = (int)doubleMinutes; - seconds = ( ( doubleMinutes - minutes ) * 60 ); + seconds = ( ( doubleMinutes - minutes ) * SECONDS_PER_MINUTE ); parseMinutes = false; } @@ -260,7 +244,6 @@ minutes = 0; } - try { seconds = Double.parseDouble( secondsText ); } @@ -268,28 +251,27 @@ seconds = 0; } } - - final Holder holder = new Holder( direction, degrees, minutes, seconds ); - holder.updateDirection(); - - return holder; + + return new LatitudeLongitudeParser( direction, degrees, minutes, seconds, maxDegrees ); } /** * Attempts to parse the specified string representing a latitude or longitude into a double containing the - * decimal representation. + * decimal representation. */ - public double parseAsDouble( String value ) - throws IllegalArgumentException { + private static double parseAsDouble( + String value, + Direction defaultDirection, + int maxDegress + ) { - Holder holder = parse( value ); + final LatitudeLongitudeParser parsed = parse( value, defaultDirection, maxDegress ); - // See http://stackoverflow.com/questions/6945008/converting-value-longitude-valuesdmscompass-direction-format-to-correspondi - //noinspection MagicNumber - double convertedDegrees = holder.getDegrees() + ( ( ( (double)holder.getSeconds() / 60 ) ) + holder.getMinutes() ) / (double)60; + // See http://stackoverflow.com/questions/6945008/converting-value-longitude-valuesdmscompass-direction-format-to-correspondi + double convertedDegrees = parsed.degrees + ( ( ( parsed.seconds / SECONDS_PER_MINUTE ) ) + parsed.minutes ) / (double)MINUTES_PER_DEGREE; - if ( ( Direction.SOUTH == holder.getDirection() ) || ( Direction.WEST == holder.getDirection() ) ) { + if ( ( Direction.SOUTH == parsed.direction ) || ( Direction.WEST == parsed.direction ) ) { convertedDegrees = -1 * convertedDegrees; } @@ -298,96 +280,103 @@ @SuppressWarnings( "AssignmentToMethodParameter" ) - private String extractDirection( - String value, - Holder holder + private static LatitudeLongitudeParser parse( + String value, + Direction defaultDirection, + int maxDegrees + ) { + + // Make sure there's no extra whitespace... + value = value.trim(); + + // NOTE: I used to try to parse the value directly into a double. I don't think this really makes sense, though, + // and quite possibly explains why so many users got confused and couldn't make this functionality work the + // way they thought it should. +// try { +// return Double.parseDouble( value ); +// } +// catch ( NumberFormatException e ) { +// // Well, keep trying to parse. +// } +// + final Pair extracted = extractDirection( value, defaultDirection ); + value = extracted.second; + + // Clean up the input.... + value = value.replace( '.', ' ' ); + value = value.replace( '\'', ' ' ); + value = value.replace( '\"', ' ' ); + value = value.trim(); + + + String[] latitudeComponents = value.split( " " ); + if ( 0 == latitudeComponents.length ) { + throw new IllegalArgumentException( "Can't parse value: " + value ); + } + + int degrees = Integer.parseInt( latitudeComponents[ 0 ].trim() ); + + int minutes; + if ( latitudeComponents.length > 1 ) { + minutes = Integer.parseInt( latitudeComponents[ 1 ].trim() ); + } + else { + minutes = 0; + } + + int seconds; + if ( latitudeComponents.length > 2 ) { + seconds = Integer.parseInt( latitudeComponents[ 2 ].trim() ); + } + else { + seconds = 0; + } + + // Ahhh, but wait. IFF the value is too large, then perhaps what the user was entering was, after all, in + // decimal format? + // NOTE: Removed this for the same reason as why I removed the blanket parsing into a double... +// if ( holder.getDegrees() > getMax() && ( holder.getMinutes() > 0 ) ) { +// holder.setDegrees( Double.parseDouble( latitudeComponents[0] + "." + latitudeComponents[1] ) ); +// } + + return new LatitudeLongitudeParser( extracted.first, degrees, minutes, seconds, maxDegrees ); + } + + + private static Pair extractDirection( + String value, + Direction defaultDirection ) { char test = value.charAt( 0 ); if ( ( 'S' == test ) || ( 's' == test ) ) { - value = value.substring( 1 ); - holder.setDirection( Direction.SOUTH ); + return new Pair<>( Direction.SOUTH, value.substring( 1 ) ); } else if ( ( 'W' == test ) || ( 'w' == test ) ) { - value = value.substring( 1 ); - holder.setDirection( Direction.WEST ); + return new Pair<>( Direction.WEST, value.substring( 1 ) ); } else if ( ( 'N' == test ) || ( 'n' == test ) ) { - value = value.substring( 1 ); - holder.setDirection( Direction.NORTH ); + return new Pair<>( Direction.NORTH, value.substring( 1 ) ); } else if ( ( 'E' == test ) || ( 'e' == test ) ) { - value = value.substring( 1 ); - holder.setDirection( Direction.EAST ); + return new Pair<>( Direction.EAST, value.substring( 1 ) ); } test = value.charAt( value.length() - 1 ); if ( ( 'S' == test ) || ( 's' == test ) ) { - value = value.substring( 0, value.length() - 1 ); - holder.setDirection( Direction.SOUTH ); + return new Pair<>( Direction.SOUTH, value.substring( 0, value.length() - 1 ) ); } else if ( ( 'W' == test ) || ( 'w' == test ) ) { - value = value.substring( 0, value.length() - 1 ); - holder.setDirection( Direction.WEST ); + return new Pair<>( Direction.WEST, value.substring( 0, value.length() - 1 ) ); } else if ( ( 'N' == test ) || ( 'n' == test ) ) { - value = value.substring( 0, value.length() - 1 ); - holder.setDirection( Direction.NORTH ); + return new Pair<>( Direction.NORTH, value.substring( 0, value.length() - 1 ) ); } else if ( ( 'E' == test ) || ( 'e' == test ) ) { - value = value.substring( 0, value.length() - 1 ); - holder.setDirection( Direction.EAST ); - } - - return value.trim(); - } - - - static class LatitudeParser - extends LatitudeLongitudeParser { - - @Override - protected double getMax() { - //noinspection MagicNumber - return 90; + return new Pair<>( Direction.EAST, value.substring( 0, value.length() - 1 ) ); } - - @Override - public double parseAsDouble( String value ) throws IllegalArgumentException { - double parsed = super.parseAsDouble( value ); - - // Latitude must be -90 <= lat <= 90 - if ( ( parsed < -90 ) || ( parsed > 90 ) ) { - parsed = 0; - } - - return parsed; - } + return new Pair<>( defaultDirection, value ); } - - - static class LongitudeParser - extends LatitudeLongitudeParser { - - @Override - protected double getMax() { - //noinspection MagicNumber - return 180; - } - - - @Override - public double parseAsDouble( String value ) throws IllegalArgumentException { - double parsed = super.parseAsDouble( value ); - - // Longitude must be -180 <= long <= 180 - if ( ( parsed < -180 ) || ( parsed > 180 ) ) { - parsed = 180; - } - - return parsed; - } - } } diff --git a/ZmanLib/src/test/java/com/gindin/zmanlib/location/UT_LatitudeLongitudeParser.java b/ZmanLib/src/test/java/com/gindin/zmanlib/location/UT_LatitudeLongitudeParser.java --- a/ZmanLib/src/test/java/com/gindin/zmanlib/location/UT_LatitudeLongitudeParser.java +++ b/ZmanLib/src/test/java/com/gindin/zmanlib/location/UT_LatitudeLongitudeParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014. Jay R. Gindin + * Copyright (c) 2022. Jay R. Gindin */ package com.gindin.zmanlib.location; @@ -16,13 +16,12 @@ /** * Test our ability to properly parse latitude & longitude. - *

- * + *

+ *

* City data comes from: http://www.infoplease.com/ipa/A0001769.html, http://www.infoplease.com/ipa/A0001796.html - * - *

+ *

+ *

* A good converter seems to be: http://www.csgnetwork.com/gpscoordconv.html - * */ @SuppressWarnings( "MagicNumber" ) @RunWith( Parameterized.class ) @@ -30,19 +29,23 @@ private static class CityInfo { - final String cityName; - final LatitudeLongitudeParser.Holder latitude; - final LatitudeLongitudeParser.Holder longitude; - final double convertedLatitude; - final double convertedLongitude; + final String cityName; + + final LatitudeLongitudeParser latitude; + + final LatitudeLongitudeParser longitude; + + final double convertedLatitude; + + final double convertedLongitude; private CityInfo( - String cityName, - LatitudeLongitudeParser.Holder latitude, - LatitudeLongitudeParser.Holder longitude, - double convertedLatitude, - double convertedLongitude + String cityName, + LatitudeLongitudeParser latitude, + LatitudeLongitudeParser longitude, + double convertedLatitude, + double convertedLongitude ) { this.cityName = cityName; this.latitude = latitude; @@ -59,96 +62,82 @@ } - /** Info for the city we're currently testing. */ - private final CityInfo cityInfoToTest; + /** + * Info for the city we're currently testing. + */ + private final CityInfo cityInfoToTest; + @Parameterized.Parameters( name = "{0}" ) - public static Collection gatherTestData() { + public static Collection gatherTestData() { - CityInfo[][] cityInfos = new CityInfo[][] { + return Arrays.asList( - { - new CityInfo( - "Aberdeen, Scotland", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.NORTH, 57, 9, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.WEST, 2, 9, 0 ), - 57.15, - -2.15 - ) - }, + new CityInfo( + "Aberdeen, Scotland", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.NORTH, 57, 9, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.WEST, 2, 9, 0 ), + 57.15, + -2.15 + ), - { - new CityInfo( - "Adelaide, Australia", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.SOUTH, 34, 55, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.EAST, 138, 36, 0 ), - -34.91666666666666666667, - 138.6 - ) - }, + new CityInfo( + "Adelaide, Australia", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.SOUTH, 34, 55, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.EAST, 138, 36, 0 ), + -34.91666666666666666667, + 138.6 + ), - { - new CityInfo( - "Tel Aviv", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.NORTH, 32, 4, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.EAST, 34, 46, 0 ), - 32.06666666666666666667, - 34.76666666666666666667 - ) - }, + new CityInfo( + "Tel Aviv", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.NORTH, 32, 4, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.EAST, 34, 46, 0 ), + 32.06666666666666666667, + 34.76666666666666666667 + ), - { - new CityInfo( - "Seattle, WA", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.NORTH, 47, 37, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.WEST, 122, 20, 0 ), - 47.61666666666666666667, - -122.33333333333333333333 - ) - }, + new CityInfo( + "Seattle, WA", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.NORTH, 47, 37, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.WEST, 122, 20, 0 ), + 47.61666666666666666667, + -122.33333333333333333333 + ), - { - new CityInfo( - "New York, NY", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.NORTH, 40, 47, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.WEST, 73, 58, 0 ), - 40.78333333333333333333, - -73.96666666666666666667 - ) - }, + new CityInfo( + "New York, NY", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.NORTH, 40, 47, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.WEST, 73, 58, 0 ), + 40.78333333333333333333, + -73.96666666666666666667 + ), - { - new CityInfo( - "San Francisco, Calif.", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.NORTH, 37, 47, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.WEST, 122, 26, 0 ), - 37.78333333333333333333, - -122.43333333333333333333 - ) - }, + new CityInfo( + "San Francisco, Calif.", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.NORTH, 37, 47, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.WEST, 122, 26, 0 ), + 37.78333333333333333333, + -122.43333333333333333333 + ), - { - new CityInfo( - "Phoenix, Ariz.", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.NORTH, 33, 29, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.WEST, 112, 4, 0 ), - 33.48333333333333333333, - -112.06666666666666666667 - ) - }, + new CityInfo( + "Phoenix, Ariz.", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.NORTH, 33, 29, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.WEST, 112, 4, 0 ), + 33.48333333333333333333, + -112.06666666666666666667 + ), - { - new CityInfo( - "Ottawa, Ont., Can.", - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.NORTH, 45, 24, 0 ), - new LatitudeLongitudeParser.Holder( LatitudeLongitudeParser.Direction.WEST, 73, 43, 0 ), - 45.4, - -73.71666666666666666667 - ) - } - }; + new CityInfo( + "Ottawa, Ont., Can.", + LatitudeLongitudeParser.latitude( LatitudeLongitudeParser.Direction.NORTH, 45, 24, 0 ), + LatitudeLongitudeParser.longitude( LatitudeLongitudeParser.Direction.WEST, 73, 43, 0 ), + 45.4, + -73.71666666666666666667 + ) + ); - return Arrays.asList( cityInfos ); } @@ -162,6 +151,7 @@ fullString( false ); } + @Test public void testFullStringParsingWithMarkers() { fullString( true ); @@ -173,6 +163,7 @@ simpleString( false ); } + @Test public void testSimpleStringParsingWithMarkers() { simpleString( true ); @@ -182,60 +173,74 @@ @Test public void fullDecimalInDegrees() { - final LatitudeLongitudeParser.Holder latitude = cityInfoToTest.latitude; - LatitudeLongitudeParser.Holder holder = LatitudeLongitudeParser.parse( - Double.toString( cityInfoToTest.convertedLatitude ), "", "", latitude.getDirection() ); - Assert.assertEquals( cityInfoToTest.cityName + " latitude.getDegrees()", latitude.getDegrees(), holder.getDegrees() ); + final LatitudeLongitudeParser latitude = cityInfoToTest.latitude; + LatitudeLongitudeParser holder = LatitudeLongitudeParser.latitude( latitude.direction, + Double.toString( cityInfoToTest.convertedLatitude ), "", "" ); + Assert.assertEquals( cityInfoToTest.cityName + " latitude.degrees", latitude.degrees, + holder.degrees ); // This gets a bit complicated because IF the actual is ONE degree larger AND we have seconds, THEN that has // happened because of rounding... - Assert.assertThat( cityInfoToTest.cityName + " latitude.getMinutes()", latitude, new DegreesWithRoundingMatcher( holder ) ); + Assert.assertThat( cityInfoToTest.cityName + " latitude.getMinutes()", latitude, + new DegreesWithRoundingMatcher( holder ) ); - if ( latitude.getSeconds() != 0 ) { - Assert.assertEquals( cityInfoToTest.cityName + " latitude.getSeconds()", latitude.getSeconds(), holder.getSeconds(), 0 ); + if ( latitude.seconds != 0 ) { + Assert.assertEquals( cityInfoToTest.cityName + " latitude.seconds", latitude.seconds, + holder.seconds, 0 ); } - final LatitudeLongitudeParser.Holder longitude = cityInfoToTest.longitude; - holder = LatitudeLongitudeParser.parse( - Double.toString( cityInfoToTest.convertedLongitude ), "", "", longitude.getDirection() ); - Assert.assertEquals( cityInfoToTest.cityName + " longitude.getDegrees()", longitude.getDegrees(), holder.getDegrees() ); - Assert.assertThat( cityInfoToTest.cityName + " longitude.getMinutes()", longitude, new DegreesWithRoundingMatcher( holder ) ); + final LatitudeLongitudeParser longitude = cityInfoToTest.longitude; + holder = LatitudeLongitudeParser.longitude( longitude.direction, + Double.toString( cityInfoToTest.convertedLongitude ), "", "" ); + Assert.assertEquals( cityInfoToTest.cityName + " longitude.degrees", longitude.degrees, + holder.degrees ); + Assert.assertThat( cityInfoToTest.cityName + " longitude.getMinutes()", longitude, + new DegreesWithRoundingMatcher( holder ) ); - if ( longitude.getSeconds() != 0 ) { - Assert.assertEquals( cityInfoToTest.cityName + " longitude.getSeconds()", longitude.getSeconds(), holder.getSeconds(), 0 ); + if ( longitude.seconds != 0 ) { + Assert.assertEquals( cityInfoToTest.cityName + " longitude.seconds", longitude.seconds, + holder.seconds, 0 ); } } private void fullString( boolean withMarkers ) { - double converted = LatitudeLongitudeParser.LATITUDE_PARSER.parseAsDouble( cityInfoToTest.latitude.toFullString( withMarkers ) ); - Assert.assertEquals( cityInfoToTest.cityName + " latitude", cityInfoToTest.convertedLatitude, converted, 0.1 ); + double converted = LatitudeLongitudeParser.latitudeValue( + cityInfoToTest.latitude.toFullString( withMarkers ) ); + Assert.assertEquals( cityInfoToTest.cityName + " latitude", cityInfoToTest.convertedLatitude, + converted, 0.1 ); - converted = LatitudeLongitudeParser.LONGITUDE_PARSER.parseAsDouble( cityInfoToTest.longitude.toFullString( withMarkers ) ); - Assert.assertEquals( cityInfoToTest.cityName + " longitude", cityInfoToTest.convertedLongitude, converted, 0.1 ); + converted = LatitudeLongitudeParser.longitudeValue( + cityInfoToTest.longitude.toFullString( withMarkers ) ); + Assert.assertEquals( cityInfoToTest.cityName + " longitude", cityInfoToTest.convertedLongitude, + converted, 0.1 ); } private void simpleString( boolean withMarkers ) { - double converted = LatitudeLongitudeParser.LATITUDE_PARSER.parseAsDouble( cityInfoToTest.latitude.toSimpleString( withMarkers ) ); - Assert.assertEquals( cityInfoToTest.cityName + " latitude", cityInfoToTest.convertedLatitude, converted, 0.1 ); + double converted = LatitudeLongitudeParser.latitudeValue( + cityInfoToTest.latitude.toSimpleString( withMarkers ) ); + Assert.assertEquals( cityInfoToTest.cityName + " latitude", cityInfoToTest.convertedLatitude, + converted, 0.1 ); - converted = LatitudeLongitudeParser.LONGITUDE_PARSER.parseAsDouble( cityInfoToTest.longitude.toSimpleString( withMarkers ) ); - Assert.assertEquals( cityInfoToTest.cityName + " longitude", cityInfoToTest.convertedLongitude, converted, 0.1 ); + converted = LatitudeLongitudeParser.longitudeValue( + cityInfoToTest.longitude.toSimpleString( withMarkers ) ); + Assert.assertEquals( cityInfoToTest.cityName + " longitude", cityInfoToTest.convertedLongitude, + converted, 0.1 ); } + private static class DegreesWithRoundingMatcher + extends BaseMatcher { + + private final LatitudeLongitudeParser actual; + + private String mismatchReason; - private static class DegreesWithRoundingMatcher - extends BaseMatcher { - - private final LatitudeLongitudeParser.Holder actual; - private String mismatchReason; - - DegreesWithRoundingMatcher(LatitudeLongitudeParser.Holder actual) { + DegreesWithRoundingMatcher( LatitudeLongitudeParser actual ) { this.actual = actual; } @@ -243,25 +248,26 @@ @Override public boolean matches( Object item ) { - LatitudeLongitudeParser.Holder expected = (LatitudeLongitudeParser.Holder)item; + LatitudeLongitudeParser expected = (LatitudeLongitudeParser)item; - int diff = expected.getMinutes() - actual.getMinutes(); + int diff = expected.minutes - actual.minutes; if ( diff == 0 ) { return true; } if ( diff == 1 ) { - if ( ( expected.getSeconds() == 0 ) && ( actual.getSeconds() > 0 ) ) { + if ( ( expected.seconds == 0 ) && ( actual.seconds > 0 ) ) { return true; } } - mismatchReason = "diff " + diff + " and expected seconds: " + expected.getSeconds() + - " but actual seconds: " + actual.getSeconds(); + mismatchReason = "diff " + diff + " and expected seconds: " + expected.seconds + + " but actual seconds: " + actual.seconds; return false; } + @Override public void describeTo( Description description ) {