65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 TomTom Search API 清理地址数据

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.10/5 (5投票s)

2018年10月4日

CPOL
viewsIcon

6758

本文介绍如何开始使用 TomTom Search API 进行地址验证和数据清理。

地址信息是全球企业最常收集的数据形式之一。它也是一种容易以不准确或不完整形式收集和存储的数据。

街道名称可能拼写错误。输入地址时可能会省略邮政编码。多个客户可能姓名相同,导致无法确定哪些地址对应哪些人。这些只是地址信息中可能出现的几种数据质量错误。

幸运的是,开发人员有一种简单的方法可以清理地址数据,而无需购买复杂的数据质量工具或获得数据工程博士学位。该解决方案是 TomTom Search API,它提供了一个结构化的地理编码调用,可以清理地址数据。它还提供准确的纬度和经度信息,可以取代未格式化的原始数据,从而提供更高的准确性和精确性。

本文介绍如何开始使用 TomTom Search API 进行地址验证和数据清理。我们将讨论地理编码和正确格式化地址数据的优势,然后通过一个示例地址清理程序,该程序利用了 TomTom 的结构化地理编码 API 调用。

地理编码和结构化地址数据的优势

如上所述,通过在线表单请求数据时,遇到数据有效性问题并不少见。在从客户请求地址数据时也是如此。拼写错误、大写错误和缺失字段都会导致明显的问题。设想这样一种情况:一家公司从中读取此数据库,以便向所有提供地址数据的客户进行邮件发送。该公司希望邮件能够到达正确的目的地,但在查看地址列表时,地址可能存在拼写错误,导致投递失败。正确地构建地址数据将是解决此问题的关键。

通过使用 TomTom 的 Search API 产品,组织可以将提供的地址数据传递到结构化的地理编码 API 调用中,如果他们获得了足够的相关地址数据来缩小搜索范围,他们就可以获得实际的(正确格式化的)地址,从而能够无误地进行邮件发送。此外,地址将被地理编码,并且他们可以将纬度和经度以及相关的地址存储在他们的数据库中。

地理编码在当今时代提供了多项不容置疑的优势——其中许多优势源于组织能够从地理层面分析其客户群。与其仅仅盯着页面上的地址数据行,不如可以查看一个地点地图,这可能会为他们的市场提供宝贵的见解。也许他们的业务在一个特定城市的某些地区比其他地区更成功,而地图数据可以帮助他们发现应将精力集中在何处。或者,也许他们在城市的一个区域已经占据了市场,但存在一些具有关键相似性但市场仍未开发的社区。在这两种情况下,地理编码都可以帮助分析客户数据。

开始使用 TomTom Search API

利用 TomTom 的结构化地理编码的第一步是设置 TomTom Search API。访问 TomTom for Developers 网站并注册,您将进入一个仪表板,您可以在其中选择添加新应用程序的选项。提供应用程序名称并选择 Search API 产品将为您提供一个 API 密钥,用于 Search API。

从仪表板可以看到,您每天可以免费使用 2,500 次 API 事务。如果 2,500 次事务不足以支持您的应用程序,您可以通过导航到仪表板的“我的积分”选项卡并选择添加积分的选项来购买积分以获得额外的事务。此外,单击标有“我的 API 事务”的选项卡将引导您进入一个页面,用于跟踪您每天的 API 事务使用情况。

添加已批准使用 Search API 产品的应用程序并收到 API 密钥后,您就可以开发您的应用程序了,该应用程序可以访问结构化的地理编码方法来清理地理编码地址。在开发利用 TomTom Search API 产品的应用程序时,一个宝贵的资源是该资源的在线文档,位于 此处

一个简单的 Java 实现

因此,让我们看一个利用 TomTom Search API 的结构化地理编码 API 调用的示例程序。为了演示此 API 调用的功能,我开发了一个简单的 Java 实现,其中 CSV 输入文件充当我们的未格式化地址数据库,CSV 输出文件充当我们的格式化地址数据库。从输入文件中,我们将逐行读取每个地址,并将输入作为结构化地理编码请求的一部分。输入文件中的每个地址都包含拼写错误、缺失字段,甚至两者都有。输入文件中的任何地址都没有存储纬度和经度。

在下面,您将看到来自 FormatAddresses.java 的代码片段。类变量设置为 API 密钥,以及 CSV 字段分隔符(逗号分隔)、换行符分隔符(用于写入输出文件)、输出文件格式的标头以及输入和输出文件名及路径。为简单起见,我将示例程序的函数写在类的主方法中,我们可以直接从 IDE 运行我们的程序。

在主方法中,我们首先创建并打开我们的输出文件,并将标头写入输出文件的第一行。在第一行末尾追加换行符后,我们可以确保现在已准备好将格式化的地址写入输出,这模拟了一个格式化的地址数据库表。下一步是读取我们的第一行并实例化一个字符串数组,按逗号分隔符拆分。这将形成一个数组,其中每个位置都包含输入文件行中的一个字段。在此之后,我们将字符串数组传递给 UnformattedAddress.java 的构造函数,在那里我们创建一个对象,该对象将我们未格式化的字段组织成 Java 对象的属性,然后这些属性可以传递给 HTTP GET 请求(我们的 API 调用)。

 

public class FormatAddresses {
  private static final String API_KEY = "YOUR-API-KEY-GOES-HERE";
  private static final String SEPARATOR = ",";
  private static final String NEW_LINE_SEPARATOR = "\n";
  private static final String HEADERS = "streetNumber, streetName, municipality, countryTertiarySubdivision, countrySecondarySubdivision, countrySubdivision, postalCode";
  private static final String INPUT_FILENAME = "C:/documents/Addresses.csv";
  private static final String OUTPUT_FILENAME = "C:/documents/FormattedAddresses.csv"

  public static void main(String[] args) {
    BufferedReader br = null;
    String line = "";

    try {
      FileWriter fileWriter = new FileWriter(OUTPUT_FILENAME);
      fileWriter.append(HEADERS);
      fileWriter.append(NEW_LINE_SEPARATOR);

      br = new BufferedReader(new FileReader(INPUT_FILENAME));
      while ((line = br.readLine()) != null) {
        String[] unformattedAddressArray = line.split(SEPARATOR);
        UnformattedAddress unformattedAddress = new UnformattedAddress(unformattedAddressArray);

        URL url = new URL("https://api.tomtom.com/search/2/structuresGeocode.JSON"
                  + "?key=" + URLEncoder.encode(API_KEY, "UTF-8")
                  + "&countryCode=" + URLEncoder.encode("US", "UTF-8")
                  + "&streetNumber=" + URLEncoder.encode(unformattedAddress.getStreetNumber(), "UTF-8")
                  + "streetName=" + URLEncoder.encode(unformattedAddress.getStreetName(), "UTF-8")
                  + "&municipality=" + URLEncoder.encode(unformattedAddress.getMunicipality(), "UTF-8")
                  + "&countrySecondarySubdivision=" + URLEncoder.encode(unformattedAddress.getCountrySecondarySubdivision(), "UTF-8")
                  + "&countrySubdivision=" + URLEncoder.encode(unformnattedAddress.getCountrySubdivision(), "UTF-8")
                  + "&postalCode=" + URLEncoder.encode(unformattedAddress.getPostalCode(), "UTF-8"));

        HttpURLConnection conn = (HttpURLConnection)url.openConnection();

        InputStream in = new BufferedInputStream(conn.getInputStream());
        String output = readStream(in);
        JSONObject jsonObject = (JSONObject) new JSONParser().parse(output.toString());
        JSONArray results = (JSONArray) jsonObject.get("results");

        for(JSONObject result : results) {
          JSONObject address = (JSONObject)result.get("address");
          JSONObject position = (JSONObject)result.get("position");

          FormattedAddress formattedAddress = new FormattedAddress(address, position);
          writeFormattedAddress(formattedAddress, fileWriter);
        }

        fileWriter.flush();
        fileWriter.close();
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      } catch (Parseexception e) {
        e.printStackTrace();
      } finally {
        br.close();
      }
    }
  }

  /** remainder of class omitted */
}

一旦我们实例化了未格式化的地址对象,我们就可以创建用于请求的 URL。正如您在 API 调用文档 中所见,执行结构化地理编码调用需要几个参数,还有更多是可选的。

必需的参数包括以下几项,我们在构建 URL 对象时提供这些参数:

  • 基本 URL:api.tomtom.com/search/
  • 版本号 2
  • 响应格式:在此特定示例中,我选择了 JSON。JSONP、JS 和 XML 也是有效的选项。
  • 国家代码:在我的示例中,我将使用 US 作为国家代码。
  • API 密钥:您可以通过在 TomTom 开发者仪表板上添加应用程序时获得的 API 密钥插入。

以下是我在此特定示例中提供的可选请求参数:

  • 街道号:如果输入文件中提供了街道号,则会将其添加到 API 请求中。如果没有,我将向调用传递一个空字符串。
  • 街道名称
  • 城市:提供的城市或城镇。
  • 国家二级行政区:提供的县。
  • 国家行政区:地址所在的州。
  • 邮政编码:提供的邮政编码。

如上面的代码所示,使用上述参数构建了请求的 URL。(有关可以利用的额外请求参数的更多信息,请访问文档。)

在我示例中的下一步是发送 HTTP 请求,解析 JSON 响应并构建一个格式化的地址对象,然后我们可以利用该对象写入我们的输出文件(模拟的干净数据库)。请参见上面的代码,了解我如何将这些话付诸实践。

例如,取以下输入,摘自我输入文件的第一行:

  • 街道号 4
  • 街道名称:Yawkey
  • 城市:Boston
  • 邮政编码2215

这当然是芬威公园的地址。但它不完整。将这些字段从输入提供给相应的参数请求后,我收到了以下 JSON 响应:

{  
   "summary":{  
      "query":"4 02215 yawkey boston",
      "queryType":"NON_NEAR",
      "queryTime":42,
      "numResults":1,
      "offset":0,
      "totalResults":1,
      "fuzzyLevel":1,
      "geoBias":{  
         "lat":42.34528579930702,
         "lon":-71.10655
      }
   },
   "results":[  
      {  
         "type":"Point Address",
         "id":"US/PAD/p0/2532244",
         "score":11.1,
         "dist":670.4458521397014,
         "address":{  
            "streetNumber":"4",
            "streetName":"Yawkey Way, Jersey St",
            "municipalitySubdivision":"Boston, Fenway",
            "municipality":"Boston, Boston University, Kenmore",
            "countrySecondarySubdivision":"Suffolk",
            "countryTertiarySubdivision":"Boston",
            "countrySubdivision":"MA",
            "postalCode":"02215",
            "extendedPostalCode":"022159103",
            "countryCode":"US",
            "country":"United States Of America",
            "countryCodeISO3":"USA",
            "freeformAddress":"4 Yawkey Way, Boston, MA 02215",
            "countrySubdivisionName":"Massachusetts"
         },
         "position":{  
            "lat":42.34679,
            "lon":-71.09865
         },
         "viewport":{  
            "topLeftPoint":{  
               "lat":42.34769,
               "lon":-71.09987
            },
            "btmRightPoint":{  
               "lat":42.34589,
               "lon":-71.09743
            }
         },
         "entryPoints":[  
            {  
               "type":"main",
               "position":{  
                  "lat":42.34671,
                  "lon":-71.09889
               }
            }
         ]
      }
   ]
}

由于我只对将特定字段保存到我的输出“数据库”感兴趣,因此我仅检索结果数组中位于根级别的定位和地址数据。正如您所看到的,这为我提供了完整的地址数据以及一个自由格式的地址字段,此外还有芬威公园的地理编码数据(纬度和经度)。我只需将此地址和定位数据写入输出文件,然后继续处理输入文件中的下一条记录。在地理编码领域可能还有另一个有趣的字段是为每个结果提供的入口点数组。主入口的确切位置会给出,这可能非常宝贵,具体取决于人们希望通过检索此数据来实现什么目标。

结论

未格式化的地址数据是一个常见的挑战。TomTom Search API 的地理编码请求功能提供了一种易于使用的解决方案,用于清理地址数据并构建地理编码位置的数据库,然后通过简单的 HTTP GET 请求即可访问该数据库。

© . All rights reserved.