Validate if a String Is a Valid Geo Coordinate – 验证字符串是否为有效地理坐标

最后修改: 2024年 1月 19日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.导言

In this tutorial, we’ll explore various approaches to validating geo coordinates and their accuracy in Java.

在本教程中,我们将探讨在 Java 中验证地理坐标及其准确性的各种方法。

2. Understand Geo Coordinates

2.了解地理坐标

Geo coordinates are typically expressed as latitude and longitude values, pinpointing locations on our spherical Earth. Latitude measures the distance north or south of the equator and ranges from -90° (South Pole) to 90° (North Pole). On the other hand, longitude measures the distance east or west of the prime meridian and spans -180° (International Date Line) to 180°.

地理坐标通常用纬度和经度值表示,精确定位球形地球上的位置。纬度测量赤道南北的距离,范围从-90°(南极)到90°(北极)。另一方面,经度测量本初子午线以东或以西的距离,范围从-180°(国际日期变更线)到180°。

3. Understanding Precision and Accuracy

3.了解精度和准确度

When checking the validity of coordinates, one crucial factor to consider is the precision of the decimal places. The level of detail expressed by a coordinate and associated with a location is indicated by precision, which is represented by the number of decimal places.

在检查坐标的有效性时,需要考虑的一个关键因素是小数位的精度。坐标表示的与位置相关的详细程度由精度表示,精度用小数位数表示。

Furthermore, rounding coordinates can cause inaccuracies, especially in applications sensitive to proximity. For example, mapping a building might require coordinates accurate to five decimal places (approximately one meter), while city-level mapping might only need two decimal places (approximately one kilometer).

此外,坐标四舍五入也会造成误差,尤其是在对距离敏感的应用中。例如,绘制建筑物的地图可能需要精确到小数点后五位(约一米)的坐标,而绘制城市级别的地图可能只需要小数点后两位(约一公里)。

4. Latitude and Longitude Formats

4.纬度和经度格式

Understanding the various formats is fundamental for both input and validation of coordinates.

了解各种格式是输入和验证坐标的基础。

4.1. Decimal Degrees (DD)

4.1.十进制度(DD)

Geo coordinates are commonly represented in decimal degrees, where both latitude and longitude are expressed as decimal values. In the DD format, valid latitude values typically range from -90 to 90, while valid longitude values range from -180 to 180. For example, the coordinates for the Eiffel Tower in Paris are approximately 48.8588445 (latitude) and 2.2943506 (longitude).

地理坐标通常以十进制度表示,其中纬度和经度均以十进制值表示。在 DD 格式中,有效纬度值的范围通常为-90 至 90,有效经度值的范围为-180 至 180。例如,巴黎埃菲尔铁塔的坐标约为 48.8588445(纬度)和 2.2943506(经度)。

4.2. Degrees, Minutes, and Seconds (DMS)

4.2.度、分、秒(DMS)

The DMS format involves degrees, minutes, and seconds, and latitude and longitude are expressed as degrees. Each degree is further divided into 60 minutes, and each minute can optionally be divided into 60 seconds. Symbols such as ° (degree), ‘ (minute), and ” (second) are used to separate these components. For instance, the coordinates for the Statue of Liberty are 40°41’21.7″N (latitude) and 74°02’40.7″W (longitude).

DMS 格式包括度、分和秒,经度和纬度用度表示。每度再分为 60 分钟,每分钟也可选择分为 60 秒。 °(度)、’(分)和””(秒)等符号用于分隔这些部分。例如,自由女神像的坐标是 40°41’21.7″N(纬度)和 74°02’40.7″W(经度)。

4.3. Military Grid Reference System (MGRS)

4.3.军用网格参照系统(MGRS)

MGRS is another coordinate format used to specify locations on the Earth’s surface. It divides the world into grid zones and provides a concise representation of them. Each section is a 6° wide zone, numbered 1 to 60, further divided into 100,000-meter squares identified by two-letter codes. Within each square, precise positions are given by “Easting” and “Northing”, both measured in meters. For example, the Great Wall of China’s Badaling section can be located as 50TMK6356175784.

MGRS 是另一种用于指定地球表面位置的坐标格式。它将世界划分为网格区,并提供简明的表示方法。每个网格区宽 6°,编号为 1 至 60,再划分为 100,000 米的方格,由双字母代码标识。在每个方格内,精确的位置用 “东经 “和 “北纬 “表示,均以米为单位。例如,中国长城八达岭段的位置为 50TMK6356175784。

5. Basic Validation With Regular Expressions

5.使用正则表达式进行基本验证

Regular expressions are powerful tools for pattern matching. We can craft a regex pattern to identify valid coordinate formats using Java’s Pattern and Matcher classes.

正则表达式是用于模式匹配的强大工具。我们可以使用 Java 的 PatternMatcher 类创建一个 regex 模式,以识别有效的坐标格式。

The first regex is crafted for DD format, where two decimal numbers are separated by a comma, with an optional space:

第一个 regex 是为 DD 格式设计的,其中两个十进制数字之间用逗号隔开,可选择空格:

public static final String DD_COORDINATE_REGEX = "^(-?\\d+\\.\\d+)(\\s*,\\s*)?(-?\\d+\\.\\d+)$";

The second regex is built to handle the DMS format. It checks for two sets of coordinates, including degrees, minutes, seconds, and cardinal directions (N, S, W, or E), allowing for flexibility in symbol usage:

第二个 regex 用于处理 DMS 格式。它检查两组坐标,包括度、分、秒和主要方向(北、南、西或东),允许灵活使用符号:

public static final String DMS_COORDINATE_REGEX = 
  "^(\\d{1,3})°(\\d{1,2})\'(\\d{1,2}(\\.\\d+)?)?\"?([NSns])(\\s*,\\s*)?
    (\\d{1,3})°(\\d{1,2})\'(\\d{1,2}(\\.\\d+)?)?\"?([WEwe])$";

The final regex caters to MGRS coordinates. The code validates patterns that consist of a UTM zone represented by two digits, a latitude band using alphabetical characters (excluding I and O), followed by two to ten digits arranged in even pairs:

最后一个 regex 针对 MGRS 坐标。该代码验证的模式包括由两位数表示的 UTM 区、使用字母字符(不包括 I 和 O)表示的纬度带,以及以偶数对排列的 2 至 10 位数:

public static final String MGRS_COORDINATE_REGEX = 
  "^\\d{1,2}[^IO]{3}(\\d{10}|\\d{8}|\\d{6}|\\d{4}|\\d{2})$";

Let’s define utility methods to validate each format:

让我们定义验证每种格式的实用方法:

boolean validateCoordinates(String coordinateString) {
    return isValidDDFormat(coordinateString) || isValidDMSFormat(coordinateString) || isValidMGRSFormat(coordinateString);
}

boolean isValidDDFormat(String coordinateString) {
    return Pattern.compile(DD_COORDINATE_REGEX).matcher(coordinateString).matches();
}

boolean isValidDMSFormat(String coordinateString) {
    return Pattern.compile(DMS_COORDINATE_REGEX).matcher(coordinateString).matches();
}

boolean isValidMGRSFormat(String coordinateString) {
    return Pattern.compile(MGRS_COORDINATE_REGEX).matcher(coordinateString).matches();
}

Regular expressions are useful for basic validation but cannot guarantee the correctness of coordinates. For instance, the DD regex doesn’t validate whether the coordinates fall within Earth’s latitude and longitude ranges.

常规表达式可用于基本验证,但无法保证坐标的正确性。例如,DD regex 无法验证坐标是否位于地球的经纬度范围内。

6. Custom Validation Logic for Complex Scenarios

6.复杂情况下的自定义验证逻辑

To handle complex scenarios, let’s break down the validation process into smaller, more explicit steps.

为了处理复杂的情况,让我们把验证过程分解成更小、更明确的步骤。

6.1. Decimal Degrees

6.1.十进制度

Let’s improve the isValidDDFormat() method to handle edge cases such as non-numerical inputs and invalid coordinates:

让我们改进 isValidDDFormat() 方法,以处理非数字输入和无效坐标等边缘情况:

boolean isValidDDFormat(String coordinateString) {
    try {
        String[] parts = coordinateString.split(",");
        if (parts.length != 2) {
            return false;
        }

        double latitude = Double.parseDouble(parts[0].trim());
        double longitude = Double.parseDouble(parts[1].trim());
        if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) {
            return false;
        }

        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

The String.split() method splits the coordinates using commas and spaces as delimiters to accommodate different input formats. This ensures there are exactly two parts (latitude and longitude). We then validate that the numeric values fall within the specified ranges.

String.split() 方法使用逗号和空格作为分隔符分割坐标,以适应不同的输入格式。这将确保坐标包含两个部分(纬度和经度)。然后,我们验证数值是否在指定范围内。

The NumberFormatException is caught to handle cases where parsing to doubles fails.

NumberFormatException 会被捕获,以处理解析为双倍值失败的情况。

6.2. Degrees, Minutes, and Seconds

6.2.度、分和秒

Next, let’s improve the isValidDMSFormat() method to split the coordinate string into degrees, minutes, seconds, and hemisphere parts, and validate each one accordingly:

接下来,让我们改进 isValidDMSFormat() 方法,将坐标字符串拆分为度、分、秒和半球部分,并相应地验证每个部分:

boolean isInvalidLatitude(int degrees, int minutes, double seconds, String hemisphere) {
    return degrees < 0 || degrees > 90 || minutes < 0 || minutes >= 60 || seconds < 0 || seconds >= 60 || 
	  (!hemisphere.equalsIgnoreCase("N") && !hemisphere.equalsIgnoreCase("S"));
}

The isInvalidLatitude() method checks whether the provided latitude components are outside of the valid ranges. It returns true (invalid) if any of the following invalid conditions are met:

isInvalidLatitude() 方法会检查所提供的纬度成分是否超出有效范围。如果满足以下任何无效条件,该方法将返回 true(无效):

  • Latitude degrees are less than 0 or greater than 90
  • Latitude minutes are less than 0 or greater than or equal to 60
  • Latitude seconds are less than 0 or greater than or equal to 60
  • Hemisphere is not “N” (North) or “S” (South)

Similarly, we’ll create the equivalent validation method for longitude:

同样,我们将为经度创建等效的验证方法:

boolean isInvalidLongitude(int degrees, int minutes, double seconds, String hemisphere) {
    return degrees < 0 || degrees > 180 || minutes < 0 || minutes >= 60 || seconds < 0 || seconds >= 60 ||
      (!hemisphere.equalsIgnoreCase("E") && !hemisphere.equalsIgnoreCase("W"));
}

The isInvalidLongitude() method checks the provided longitude components, returning true (invalid) if any of the following invalid conditions are met:

isInvalidLongitude()方法会检查所提供的经度组件,如果满足以下任何无效条件,则返回true(无效):

  • Longitude degrees are less than 0 or greater than 180
  • Longitude minutes are less than 0 or greater than or equal to 60
  • Longitude seconds are less than 0 or greater than or equal to 60
  • Hemisphere is not “E” (East) or “W” (West)

Lastly, let’s leverage both validation methods to validate DMS coordinates:

最后,让我们利用这两种验证方法来验证 DMS 坐标:

boolean isValidDMSFormatWithCustomValidation(String coordinateString) {
    try {
        String[] dmsParts = coordinateString.split("[°',]");
        if (dmsParts.length > 6) {
            return false;
        }

        int degreesLatitude = Integer.parseInt(dmsParts[0].trim());
        int minutesLatitude = Integer.parseInt(dmsParts[1].trim());
        String[] secondPartsLatitude = dmsParts[2].split("\"");
        double secondsLatitude = secondPartsLatitude.length > 1 ? Double.parseDouble(secondPartsLatitude[0].trim()) : 0.0;
        String hemisphereLatitude = secondPartsLatitude.length > 1 ? secondPartsLatitude[1] : dmsParts[2];

        int degreesLongitude = Integer.parseInt(dmsParts[3].trim());
        int minutesLongitude = Integer.parseInt(dmsParts[4].trim());
        String[] secondPartsLongitude = dmsParts[5].split("\"");
        double secondsLongitude = secondPartsLongitude.length > 1 ? Double.parseDouble(secondPartsLongitude[0].trim()) : 0.0;
        String hemisphereLongitude = secondPartsLongitude.length > 1 ? secondPartsLongitude[1] : dmsParts[5];

        if (isInvalidLatitude(degreesLatitude, minutesLatitude, secondsLatitude, hemisphereLatitude) ||
          isInvalidLongitude(degreesLongitude, minutesLongitude, secondsLongitude, hemisphereLongitude)) {
            return false;
        }

        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

The isValidDMSFormatWithCustomValidation() method splits the input into its latitudinal and longitudinal components and validates them separately using the helper methods. If all checks pass, the DMS format is considered valid, and the method returns true (valid).

isValidDMSFormatWithCustomValidation() 方法将输入分割成经纬度部分,并使用辅助方法分别进行验证。如果所有检查都通过,则认为 DMS 格式有效,该方法将返回 true(有效)。

7. Conclusion

7.结论

In this article, we’ve explored two distinct approaches to validating geo coordinates. Regular expressions offer a quick solution for basic validation, while the custom validation logic offers flexibility and error feedback, making it suitable for complex scenarios.

在本文中,我们探讨了验证地理坐标的两种不同方法。正则表达式为基本验证提供了快速解决方案,而自定义验证逻辑则提供了灵活性和错误反馈,使其适用于复杂场景。

As always, the source code for the examples is available over on GitHub.

与往常一样,这些示例的源代码可在 GitHub 上获取。