如何使用AngularJS和PHP为任何位置生成短而独特的数字地址

介绍

邮政地址通常很长,有时很难记住。在许多情况下,需要较短的地址。例如,能够发送仅由几个字符组成的短地址可以确保更快地提供紧急救护车服务。Pieter Geelen和Harold Goddijn 于2001年开发了Mapcode系统,以便为世界上任何物理地址创建简短地址。

在本教程中,您将开发一个Web应用程序,该应用程序使用Google Maps API为您选择的任何地址生成一个简短的数字地址。您将通过从GitHub克隆此应用程序的基本代码,然后向其中添加使其完全正常运行的代码来实现此目的。此应用程序还可以从给定的地图代码中检索原始物理地址。

先决条件

要完成本教程,您需要具备以下条件:

  • 访问Ubuntu 18.04服务器。此服务器应具有具有权限且配置了防火墙的具有sudo权限的非root用户。要进行此设置,没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后再购买服务器
  • 机器上安装了LAMP堆栈。这是必要的,因为您将在本教程中开发的应用程序使用AngularJS和PHP,并且应用程序生成的数字地址将存储在MySQL数据库中。
  • 在您的服务器上安装Git。您可以按照教程如何在Ubuntu 18.04上快速安装Git来安装和设置Git。

第1步 - 获取Google API密钥

在本教程中,您将使用JavaScript创建Google Maps的界面。Google会分配API密钥,以便开发人员可以在Google地图上使用JavaScript API,您需要获取该API并将其添加到您的网络应用程序代码中。

要获取自己的API密钥,请访问Google的“获取API密钥”页面。单击步骤1中的GET STARTED按钮,将打开一个弹出窗口,如下图所示:

单击复选框选择“ 地图”,然后单击“ 继续”。如果您尚未登录Google帐户,系统会要求您这样做。然后,窗口会要求您提供项目的名称,这可以是您想要的任何名称:

在此之后,它会要求您输入结算信息。请注意,Google提供API密钥作为免费试用的一部分,但它要求您设置并启用结算以便检索它们。

输入此信息后,您的API密钥将显示在屏幕上。将其复制并存储在可以轻松检索的位置,因为稍后您需要将其添加到项目代码中。

获取API密钥后,您可以通过创建MySQL数据库来开始构建应用程序的基础。

第2步 - 创建数据库

本教程中描述的Web应用程序接受来自用户的地址,并为其生成地图代码以及指定位置的纬度和经度。您将把这些数据存储在MySQL数据库中,以便稍后通过输入相应的数字地址来检索它。

首先打开MySQL shell并使用您的密码进行身份验证:

mysql -u root -p

在提示符下,使用以下命令创建一个名为digitaladdress的数据库:

CREATE DATABASE IF NOT EXISTS `digitaladdress`;

接下来,选择此新数据库,以便在其中创建表:

USE `digitaladdress`;

选择digitaladdress数据库后,创建一个名为locations的表,以存储应用程序将根据此数据创建的物理地址,经度,纬度和地图代码。运行以下CREATE TABLE语句以在数据库中创建locations表:

CREATE TABLE `locations` (
  `digitaladdress` varchar(50) DEFAULT NULL,
  `state` varchar(30) DEFAULT NULL,
  `zip` varchar(30) DEFAULT NULL,
  `street` varchar(30) DEFAULT NULL,
  `town` varchar(30) DEFAULT NULL,
  `house` varchar(30) DEFAULT NULL,
  `latitude` varchar(30) DEFAULT NULL,
  `longitude` varchar(30) DEFAULT NULL,
  KEY `digitaladdress` (`digitaladdress`)
);

这个表有八列:digitaladdressstatezipstreettownhouselatitude,和longitude。第一列中,使用KEY命令来将digitaladdress编入索引。MySQL中的索引功能与它们在百科全书或其他参考工作中的工作方式类似。您或您的应用程序发出包含的查询时WHERE声明,MySQL逐行读取每列中的每个条目,这可能成为一个资源密集程度极高的过程,因为您的表累积了越来越多的条目。索引像这样的列从列中获取数据并按字母顺序存储在一个单独的位置,这意味着MySQL不必查看表中的每一行。它只需要在索引中找到您要查找的数据,然后跳转到表中的相应行。

添加此表后,退出MySQL提示符:

exit

通过设置数据库和表格以及Google Maps API密钥,您就可以自行创建项目了。

第3步 - 创建项目

如介绍中所述,我们将从GitHub克隆此项目的基本代码,然后添加一些额外的代码以使应用程序正常运行。这样做的原因是为了加快启动应用程序运行的过程,而不是引导您完成创建每个文件并自行添加所有代码的过程。它还允许我们专注于添加和理解允许应用程序与Google Maps和Mapcode API进行通信的代码。

您可以在此GitHub项目页面上找到完整项目的框架代码。使用以下git命令将项目克隆到服务器:

git clone https://github.com/do-community/digiaddress.git

这将在主目录中创建一个名为digiaddress的新文件夹。将此目录移动到服务器的Web根目录。如果您按照先决条件中链接的LAMP堆栈教程进行操作,那么这就将是以下/var/www/html目录:

sudo mv digiaddress/ /var/www/html/

这个项目包含几个PHP和JS文件,稍后您将在本教程中添加一些代码。要查看目录结构,请首先使用apt来安装tree包:

sudo apt install tree

然后运行带有作为参数给出的digiaddress目录的tree命令:

tree /var/www/html/digiaddress/
digiaddress/
├── README.md
├── db.php
├── fetchaddress.php
├── findaddress.php
├── generateDigitalAddress.php
├── geoimplement.php
├── index.php
└── js
    ├── createDigitialAddressApp.js
    └── findAddressApp.js

您可以从此输出中看到该项目包含六个PHP文件和两个JavaScript文件。这些文件共同创建了应用程序的两个主要功能:从物理地址创建地图代码,以及解码地图代码以检索原始物理地址。以下文件启用第一个功能:

  • index.php
  • geoimplement.php
  • generateDigitialAddress.php
  • db.php
  • createDigitialAddressApp.js

index.php文件包含应用程序用户界面(UI)的代码,该代码由用户可以输入物理地址的表单组成。只要用户提交表单,该index.php文件就会调用该geoimplement.php文件。geoimplement.php拨打Google Maps API并将地址传递给它。然后,Google服务器会使用包含指定地址信息的JSON进行响应,包括其纬度和经度。然后将该信息传递给调用Mapcode API 的 generateDigitalAddress.php 文件,以获得给定位置的地图代码,如其纬度和经度所指定的。然后,生成的映射代码以及纬度,经度和物理地址将存储在您在步骤2中创建的数据库中。db.php充当此操作的帮助程序。该createDigitalAddressApp.js 文件执行许多操作来控制应用程序中看到的UX元素,包括在Google Maps界面上设置标记和边界矩形。

其余三个文件启用应用程序的第二个功能 - 即从给定的mapcode中检索物理地址:

  • findaddress.php
  • fetchaddress.php
  • findAddressApp.js

findaddress.php文件定义了应用程序UI,它与index.php中定义的UI不同。应用程序接受先前生成的映射代码作为输入,并显示存储在数据库中的相应物理地址。每当用户提交此表单时,findaddress.phpfetchaddress.php发送一个要求,然后从数据库中检索相应的映射代码。该findAddressApp.js文件包含用于在Google Maps界面上设置标记和边界矩形的帮助程序代码。

通过在浏览器中访问http://your_server_ip/digiaddress``your_server_ip来测试安装,确保更改以反映服务器的IP地址。

注意:如果您不知道服务器的IP地址,则可以运行以下curl命令。此命令将打印icanhazip.com的页面内容,该网站显示访问它的机器的IP地址:

curl http://icanhazip.com

在那里,您将在浏览器窗口的顶部看到此标题:

Generate Digital Address

这确认您已正确下载项目文件。有了它,让我们继续开发应用程序的主要功能:生成地图代码。

第4步 - 开发应用程序的UI

虽然应用程序界面的样板代码包含在您在上一步中下载的文件中,但您仍需要对其中一些文件进行一些更改和添加,以使应用程序正常运行并吸引用户。我们将开始更新代码以开发应用程序的UI。

首先使用你喜欢的编辑器打开index.php文件。在这里,我们将使用nano

nano /var/www/html/digiaddress/index.php

查找以下代码行:

. . .
<script async defer src="https://maps.googleapis.com/maps/api/js?key=<YOUR KEY>"></script>
. . .

用您在步骤1中获得的Google API密钥替换<YOUR KEY>。添加API密钥后,该行应与以下内容类似:

. . .
<script async defer src="https://maps.googleapis.com/maps/api/js?key=ExampleAPIKeyH2vITfv1eIHbfka9ym634Esw7u"></script>
. . .

接下来,在index.php文件中找到以下注释:

. . .
            <!-- add form code here -->
. . .

我们将在此评论下面添加几行代码,这将创建一个表单,用户可以在其中输入应用程序将用于生成地图代码的物理位置的地址。在此评论下,添加以下突出显示的代码,在代码顶部创建一个名为Enter Address的标题:

. . .
            <!-- add form code here -->
​
            <div class="form-border spacing-top">
                <div class="card-header" style="background:#cc0001; color:#ffff">
                    <h5>Enter Address</h5>
                </div>
                <div class="extra-padding">
. . .

在此下方,添加以下HTML代码。这将创建一个包含五个文本字段(及其相应标签)的表单,用户将在其中输入其信息:

                . . .
                <form>
                        <div class="form-group input-group-sm">
                            <label for="state">State</label>
                            <input type="text" class="form-control rounded-0 textbox-border" id="state"
                                   placeholder="" ng-model="address.state"/>
                        </div>
                        <div class="form-group input-group-sm">
                            <label for="zip" class="animated-label">Zip</label>
                            <input type="text" class="form-control rounded-0 textbox-depth textbox-border"
                                   id="zip" ng-model="address.zip" disabled="disabled"/>
                        </div>
                        <div class="form-group input-group-sm">
                            <label for="town">Town</label>
                            <input type="text" class="form-control rounded-0 textbox-border"
                                   id="town" ng-model="address.town" disabled="disabled"/>
                        </div>
                        <div class="form-group input-group-sm">
                            <label for="street">Street</label>
                            <input type="text" class="form-control rounded-0 textbox-border" id="street"
                                   placeholder="" ng-model="address.street" disabled="disabled"/>
                        </div>
                        <div class="form-group input-group-sm">
                            <label for="house">House</label>
                            <input type="text" class="form-control rounded-0 textbox-border" id="house"
                                   placeholder="" ng-model="address.house" disabled="disabled"/>
                        </div>
                 . . .

在表单代码下方,添加以下行。这些创建了两个隐藏的控件,这些控件传递从通过表单提交的任何地址派生的纬度和经度信息:

                            . . .
                            <div class="form-group input-group-sm">
                                <input type="hidden" ng-model="address.lat"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <input type="hidden" ng-model="address.long"/>
                            </div>
                            . . .

最后,通过添加以下代码来关闭此部分。这将创建一个Generate按钮,允许用户提交表单:

                            . . .
                            <button type="submit" disabled="disabled" class="btn btn-color btn-block rounded-0" id="generate"
                                    style="color:#ffff;background-color: #cc0001;">Generate
                            </button>
                    </form>
                </div>
            </div>
        . . .

添加这些元素后,该文件的这一部分应与此匹配:

. . .
            <!-- add form code here -->
​
            <div class="form-border spacing-top">
                <div class="card-header" style="background:#cc0001; color:#ffff">
                    <h5>Enter Address</h5>
                </div>
                <div class="extra-padding">
                    <form>    
                            <div class="form-group input-group-sm">
                                <label for="state">State</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="zip" class="animated-label">Zip</label>
                                <input type="text" class="form-control rounded-0 textbox-depth textbox-border"
                                       id="zip" ng-model="address.zip" disabled="disabled"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="town">Town</label>
                                <input type="text" class="form-control rounded-0 textbox-border "
                                       id="town" ng-model="address.town" disabled="disabled"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="street">Street</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="street"
                                       placeholder="" ng-model="address.street" disabled="disabled"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="house">House</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="house"
                                       placeholder="" ng-model="address.house" disabled="disabled"/>
                            </div>
​
                            <div class="form-group input-group-sm">
                                <input type="hidden" ng-model="address.lat"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <input type="hidden" ng-model="address.long"/>
                            </div>
                            <button type="submit" disabled="disabled" class="btn btn-color btn-block rounded-0" id="generate"
                                    style="color:#ffff;background-color: #cc0001;">Generate
                            </button>
                    </form>
                </div>
            </div>
            <br>
        </div>
​
        <!-- add google map control -->
                    . . .

通过依次按下CTRL+OENTER来保存文件,然后再次在浏览器中访问该应用程序:

http://your_server_ip/digiaddress

您将看到新添加的表单字段和生成按钮,应用程序应如下所示:

此时,如果您在表单中输入地址信息并尝试单击“ 生成”按钮,则不会发生任何事情。我们稍后将添加地图代码生成功能,但我们首先关注通过添加用户可以与之交互的地图来使该页面更具视觉吸引力。

第5步 - 添加Google地图控件

当地图通过Google Maps JavaScript API显示在网站上时,它们包含用户界面功能,允许访问者与他们看到的地图进行互动。这些功能称为控件。我们将继续编辑该index.php文件,将Google地图控件添加到此应用中,完成后,用户将能够查看输入表单旁边的地图,将其拖动以查看不同位置,放大和缩小,以及在Google之间切换地图,卫星和街景。

index.php文件中找到以下注释:

. . .
<!-- add google map control -->
. . .

在此评论下方添加以下突出显示的代码:

. . .
        <!-- add google map control -->
​
        <div class="col-sm-8 map-align" ng-init="initMap()">
            <div id="map" class="extra-padding" style="height: 100%;
            margin-bottom: 15px;"></div>
            <label id="geocoordinates" ng-show="latlng" ng-model="lt"></label><br/>
            <label id="geoaddress" ng-show="address" ng-model="padd"></label>
            </div>
        </div>
. . .

保存文件,然后再次在浏览器中访问该应用程序。您将看到以下内容:

如您所见,我们已成功将地图添加到应用程序中。您可以拖动地图以聚焦在不同位置,放大和缩小,以及在地图,卫星和街道视图之间切换。回顾刚刚添加的代码,请注意我们还添加了两个标签控件,它们将显示在表单上输入的地理坐标和物理地址:

            . . .
            <label id="geocoordinates" ng-show="latlng" ng-model="lt"></label><br/>
            <label id="geoaddress" ng-show="address" ng-model="padd"></label>
            . . .

在浏览器中再次访问该应用程序,然后在第一个字段中输入状态名称。将文本光标移动到下一个字段时,不会显示纬度和经度标签,地图上显示的位置也不会更改以反映您输入的信息。让我们启用这些行为。

第6步 - 添加事件监听器

向应用程序添加交互元素有助于保持用户的参与。我们将通过使用事件侦听器在此应用程序中实现一些交互行为。

一个事件是发生在网页上的任何行动。事件可以是用户或浏览器本身完成的事情。常见事件的例子有:

  • 单击HTML按钮
  • 更改输入字段的内容
  • 将焦点从一个页面元素更改为另一个页面元素

一个事件监听器是一个指令,它讲述了一个程序在特定事件发生采取某种行动。在AngularJS中,事件侦听器使用通常遵循以下格式的指令进行定义:

ng-event_type=expression

在此步骤中,我们将添加一个事件侦听器,它有助于在用户提交表单时将用户输入的信息处理到mapcode中。我们还将添加几个事件监听器,使应用程序更具交互性。具体来说,我们将使用这些侦听器更改应用程序映射中显示的位置,放置标记,并在用户在表单中输入信息时在该位置周围绘制一个矩形。我们将把这些事件侦听器添加到index.php,因此如果您关闭它,请再次打开该文件:

nano /var/www/html/digiaddress/index.php

向下滚动到我们添加的第一批代码,然后找到以<form>代码开头的块。它看起来像这样:

                . . .
                    <form>
                            <div class="form-group input-group-sm">
                                <label for="state">State</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="zip" class="animated-label">Zip</label>
                                <input type="text" class="form-control rounded-0 textbox-depth textbox-border"
                                       id="zip" ng-model="address.zip" disabled="disabled"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="town">Town</label>
                                <input type="text" class="form-control rounded-0 textbox-border"
                                       id="town" ng-model="address.town" disabled="disabled"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="street">Street</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="street"
                                       placeholder="" ng-model="address.street" disabled="disabled"/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="house">House</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="house"
                                       placeholder="" ng-model="address.house" disabled="disabled"/>
                            </div>
                    </form>
. . .

首先,将以下突出显示的事件侦听器添加到开放的<form>标记。此代码告诉应用程序在用户通过表单提交信息时调用该processForm函数。在createDigitalAddressApp.js文件中定义processForm,并用作辅助函数,将用户提交的信息发送到适当的文件,然后将其处理为mapcode。我们将在第7步中仔细研究这个函数:

                . . .
                    <form ng-submit="processForm()" class="custom-form">
                            <div class="form-group input-group-sm">
                                <label for="state">State</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"
                            </div>
                . . .

接下来,通过添加几个blur事件侦听器继续编辑此块。一个blur在一个给定的页面元素失去焦点时发生的事件。将以下突出显示的行添加到form块的input标记中。这些行告诉应用程序在用户的焦点偏离我们在步骤4中创建的相应表单字段时调用该geocodeAddress函数。请注意,您还必须删除关闭每个input标记的斜杠和大于号(/>)。如果不这样做,将阻止应用正确注册blur事件:

                . . .
                <form ng-submit="processForm()" class="custom-form">
                            <div class="form-group input-group-sm">
                                <label for="state">State</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"
                                       ng-blur="geocodeAddress(address,'state')" required=""/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="zip" class="animated-label">Zip</label>
                                <input type="text" class="form-control rounded-0 textbox-depth textbox-border"
                                       id="zip" ng-model="address.zip" disabled="disabled"
                                       ng-blur="geocodeAddress(address,'zip')" required=""/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="town">Town</label>
                                <input type="text" class="form-control rounded-0 textbox-border"
                                       id="town" ng-model="address.town" disabled="disabled"
                                       ng-blur="geocodeAddress(address,'town')" required=""/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="street">Street</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="street"
                                       placeholder="" ng-model="address.street" disabled="disabled"
                                       ng-blur="geocodeAddress(address,'street')" required=""/>
                            </div>
                            <div class="form-group input-group-sm">
                                <label for="house">House</label>
                                <input type="text" class="form-control rounded-0 textbox-border" id="house"
                                       placeholder="" ng-model="address.house" disabled="disabled"
                                       ng-blur="geocodeAddress(address,'house')" required=""/>
                            </div>
. . .

这些新行中的第一行 - ng-blur="geocodeAddress(address,'state')" required=""/>转换为“当用户的焦点偏离'状态'字段时,调用该geocodeAddress函数。” 其他新线也会调用geocodeAddress,尽管用户的焦点会偏离各自的字段。

processForm函数一样,geocodeAddresscreateDigitalAddressApp.js文件中被声明,但该文件中还没有任何代码定义它。我们将完成此功能,以便在发生这些blur事件后放置标记并在应用程序图上绘制一个矩形,以反映输入到表单中的信息。我们还将添加一些代码来获取地址信息并将其处理为mapcode。

保存并关闭index.php文件(按下CTRL+XYENTER),然后打开该createDigitalAddressApp.js文件:

nano /var/www/html/digiaddress/js/createDigitalAddressApp.js

在此文件中,找到以下行:

. . .
$scope.geocodeAddress = function (address, field) {
. . .

这一行是我们声明geocodeAddress函数的地方。在这下面几行,我们声明一个名为fullAddress的变量,该变量根据用户输入到应用程序表单字段中的信息构造一个人类可读的邮件地址。这是通过一系列if声明完成的:

. . .
var fullAddress = "";

    if (address ['house']) {
        angular.element(document.getElementById('generate'))[0].disabled = false;
            fullAddress = address ['house'] + ",";
                }
    if (address ['town']) {
        angular.element(document.getElementById('street'))[0].disabled = false;
            fullAddress = fullAddress + address ['town'] + ",";
    }
    if (address ['street']) {
        angular.element(document.getElementById('house'))[0].disabled = false;
            fullAddress = fullAddress + address ['street'] + ",";
    }
    if (address ['state']) {
        angular.element(document.getElementById('zip'))[0].disabled = false;
            fullAddress = fullAddress + address ['state'] + " ";
    }
    if (address ['zip']) {
        angular.element(document.getElementById('town'))[0].disabled = false;
            fullAddress = fullAddress + address ['zip'];
    }
. . .

在这些行之后直接发表以下评论:

. . .
// add code for locating the address on Google maps
. . .

在此注释下面添加以下行,该行检查fullAddress是否为null以外的任何值:

                . . .
                if (fullAddress !== "") {
                . . .

在此行下方添加以下代码。如果geoimplement.php不为null,此代码使用HTTP POST方法将输入到表单中的信息提交给文件fullAddress

                    . . .
                    $http({
                        method: 'POST',
                        url: 'geoimplement.php',
                        data: {address: fullAddress},
                        headers: {'Content-Type': 'application/x-www-form-urlencoded'}

                    }).then(function successCallback(results) {
                    . . .

接下来,添加以下行来检查PHP调用是否成功返回:

                        . . .
                        if (results.data !== "false") {
                        . . .

如果成功返回PHP调用,我们将能够处理结果。添加以下行,通过调用在createDigitalAddressApp.js文件顶部定义的removeRectangle函数,删除先前在地图上绘制的任何边界矩形:

                            . . .
                            removeRectangle();
                            . . .

在该removeRectangle();行下,添加以下四行,这将创建指向地图控件上新位置的标记:

                            . . .
                            new google.maps.Marker({
                                map: locationMap,
                                position: results.data.geometry.location
                            });
                            . . .

然后添加以下代码,从结果中获取纬度和经度信息,并使用我们在步骤5中的index.php文件中创建的两个HTML标签显示它:

                            . . .
                            lat = results.data.geometry.location.lat;
                            lng = results.data.geometry.location.lng;

                            $scope.address.lat = lat;
                            $scope.address.lng = lng;

                            geoCoordLabel = angular.element(document.querySelector('#geocoordinates'));
                            geoCoordLabel.html("Geo Coordinate: " + lat + "," + lng);

                            geoAddressLabel = angular.element(document.querySelector('#geoaddress'));
                            geoAddressLabel.html("Geo Address: " + fullAddress);

                            $scope.latlng = true;
                            . . .

最后,在这些行下面添加以下内容。此代码创建一个视口,在地图上标记新的边界矩形:

                            . . .
                            if (results.data.geometry.viewport) {

                                rectangle = new google.maps.Rectangle({
                                    strokeColor: '#FF0000',
                                    strokeOpacity: 0.8,
                                    strokeWeight: 0.5,
                                    fillColor: '#FF0000',
                                    fillOpacity: 0.35,
                                    map: locationMap,
                                    bounds: {
                                        north: results.data.geometry.viewport.northeast.lat,
                                        south: results.data.geometry.viewport.southwest.lat,
                                        east: results.data.geometry.viewport.northeast.lng,
                                        west: results.data.geometry.viewport.southwest.lng
                                    }
                                });

                                var googleBounds = new google.maps.LatLngBounds(results.data.geometry.viewport.southwest, results.data.geometry.viewport.northeast);

                                locationMap.setCenter(new google.maps.LatLng(lat, lng));
                                locationMap.fitBounds(googleBounds);
                            }
                        } else {
                            errorLabel = angular.element(document.querySelector('#lt'));
                            errorLabel.html("Place not found.");
                            $scope.latlng = true;
                            removeRectangle();
                        }

                    }, function errorCallback(results) {
                       console.log(results);
                    });
                }
                . . .

添加此内容后,该文件的此部分将如下所示:

                . . .
                // add code for locating the address on Google maps
                if (fullAddress !== "") {
                    $http({
                        method: 'POST',
                        url: 'geoimplement.php',
                        data: {address: fullAddress},
                        headers: {'Content-Type': 'application/x-www-form-urlencoded'}

                    }).then(function successCallback(results) {

                        if (results.data !== "false") {
                            removeRectangle();

                            new google.maps.Marker({
                                map: locationMap,
                                position: results.data.geometry.location
                            });

                            lat = results.data.geometry.location.lat;
                            lng = results.data.geometry.location.lng;

                            $scope.address.lat = lat;
                            $scope.address.lng = lng;

                            geoCoordLabel = angular.element(document.querySelector('#geocoordinates'));
                            geoCoordLabel.html("Geo Coordinate: " + lat + "," + lng);

                            geoAddressLabel = angular.element(document.querySelector('#geoaddress'));
                            geoAddressLabel.html("Geo Address: " + fullAddress);

                            $scope.latlng = true;

                            if (results.data.geometry.viewport) {

                                rectangle = new google.maps.Rectangle({
                                    strokeColor: '#FF0000',
                                    strokeOpacity: 0.8,
                                    strokeWeight: 0.5,
                                    fillColor: '#FF0000',
                                    fillOpacity: 0.35,
                                    map: locationMap,
                                    bounds: {
                                        north: results.data.geometry.viewport.northeast.lat,
                                        south: results.data.geometry.viewport.southwest.lat,
                                        east: results.data.geometry.viewport.northeast.lng,
                                        west: results.data.geometry.viewport.southwest.lng
                                    }
                                });

                                var googleBounds = new google.maps.LatLngBounds(results.data.geometry.viewport.southwest, results.data.geometry.viewport.northeast);

                                locationMap.setCenter(new google.maps.LatLng(lat, lng));
                                locationMap.fitBounds(googleBounds);
                            }
                        } else {
                            errorLabel = angular.element(document.querySelector('#lt'));
                            errorLabel.html("Place not found.");
                            $scope.latlng = true;
                            removeRectangle();
                        }

                    }, function errorCallback(results) {
                       console.log(results);
                    });
                }
                . . .

保存文件,但暂时保持打开状态。如果您再次在浏览器中访问该应用程序,则不会看到其外观或行为的任何新变化。同样,如果您要输入地址并单击“ 生成”按钮,则应用程序仍然不会生成或显示地图代码。这是因为我们必须在mapcode功能运行之前编辑一些文件。让我们继续进行这些更改,并仔细研究这些地图代码是如何生成的。

第7步 - 了解地图代码生成

在查看createDigitalAddressApp.js文件的同时,滚动浏览您在上一步中添加的代码部分,以查找获取通过表单提交的信息并将其处理为唯一地图代码的代码。每当用户单击Generate按钮时,index.php文件中的代码都会提交表单并调用该processForm函数,该函数在以下createDigitalAddressApp.js位置定义:

. . .
$scope.processForm = function () {
. . .

首先使用processForm然后对generateDigitalAddress.php文件发送HTTP POST :

. . .
$http({
    method: 'POST',
    url: 'generateDigitalAddress.php',
    data: $scope.address,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function (response) {
. . .

Stichting Mapcode Foundation提供了从生成物理地址mapcodes作为一个免费的网络服务的API。要了解对Mapcode Web服务的此调用如何工作,请关闭createDigitalAddressApp.js并打开该generateDigitialAddress.php文件:

nano /var/www/html/digiaddress/generateDigitalAddress.php

在文件的顶部,您将看到以下内容:

<?php
include("db.php");
. . .

行读取include("db.php");告诉PHP 将保存在generateDigitalAddress.php文件的db.php文件中所有文本,代码和标记都包含进去。db.php保存了您在步骤2中创建的MySQL数据库的登录凭据,并通过将其包含在generateDigitalAddress.php内,我们可以将通过表单提交的任何地址信息添加到数据库中。

在此include声明下面还有几行根据提交的createDigitalAddressApp.js请求获取纬度和经度信息:

. . .
$data = json_decode(file_get_contents("php://input"));
$lat = $data->lat;
$long = $data->lng;
. . .

generateDigitalAddress.php文件中查找以下注释。

. . .
// call to mapcode web service
. . .

在此评论下方添加以下代码行。此代码调用Mapcode API,发送latlong作为参数。

. . .
// call to mapcode web service
$digitaldata = file_get_contents("https://api.mapcode.com/mapcode/codes/".$lat.",".$long."?include=territory,alphabet&allowLog=true&client=web");
. . .

Web服务返回分配给digitaldata的JSON数据,以下语句解码该JSON:

. . .
$digitalAddress["status"] = json_decode($digitaldata, TRUE)['local']['territory']." ".json_decode($digitaldata, TRUE)['local']['mapcode'];
. . .

这将返回用户指定位置的mapcode。然后,以下行将此信息存储在数据库中:

. . .
$obj = new databaseConnection();

$conn = $obj->dbConnect();

$obj->insertLocation($conn, $digitalAddress["status"],$data->state,$data->zip,$data->street,$data->town,$data->house,$lat,$long);
. . .

然后,最后一行将mapcode回传给调用函数:

. . .
echo json_encode($digitalAddress);

保存并关闭此文件,然后重新打开createDigitalAddressApp.js

nano /var/www/html/digiaddress/js/createDigitalAddressApp.js

成功检索到地图代码后,createDigitalAddressApp.js文件中的以下行会在对话框中将其显示给用户:

. . .
digiAddress = response.data.status;
. . .
$('#digitalAddressDialog').modal('show');
. . .

虽然您确实添加了新的代码行至generateDigitalAddress.php,但在浏览器中访问应用程序并与其进行交互时,您仍然看不到任何功能更改。这是因为您尚未将Google API密钥添加到该geoimplement.php文件中,从而实际调用了Google Maps API。

第8步 - 启用对Google Maps API的调用

此应用程序依赖于Google Maps API将物理地址转换为适当的纬度和经度坐标。然后将它们传递给Mapcode API,Mapcode API使用它们生成mapcode。因此,如果应用程序无法与Google Maps API通信以生成位置的纬度和经度,则生成地图代码的任何尝试都将失败。

回想一下第6步,在构建address数据之后,我们通过createDigitalAddressApp.js文件中的HTTP POST请求传递结果:

$http({
    method: 'POST',
    url: 'geoimplement.php',
    data: {address: fullAddress},
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function successCallback(results) {

此代码块将用户输入的地址数据发送到包含调用Google Maps API的代码的geoimplement.php文件。继续打开此文件:

nano /var/www/html/digiaddress/geoimplement.php

您将看到它首先解码通过POST请求收到的address内容:

. . .
$data=json_decode(file_get_contents("php://input"));
. . .

然后它将address输入数据的字段传递给一个geocode函数,该函数返回地址上的地理信息:

. . .
$result = geocode($data->address);
. . .

然后结果回显给调用者:

. . .
echo json_encode($result);
. . .

geocode函数将address编码并将其与您的应用程序密钥一起传递到Google Maps API:

. . .
// url encode the address
$address = urlencode($address);

// google map geocode api url
$url = "https://maps.googleapis.com/maps/api/geocode/json?address={$address}&key=<YOUR KEY>";
. . .

在滚动之前,请继续将API密钥添加到注释// google map geocode api url下:

. . .
// google map geocode api url
$url = "https://maps.googleapis.com/maps/api/geocode/json?address={$address}&key=ExampleAPIKeyH2vITfv1eIHbfka9ym634Esw7u";
. . .

将呼叫发送到Google Maps API后,响应将被解码,其值将由函数返回:

. . .
// get the json response
$resp_json = file_get_contents($url);

// decode the json
$resp = json_decode($resp_json, true);

if ($resp['status'] == 'OK') {
    return $resp['results'][0];
} else {
    return false;
}
. . .

保存此文件,然后再次访问您的应用程序。在状态字段中输入US-NY然后单击TAB以将输入焦点更改为下一个字段。您将看到以下输出:

请注意,您在表单中输入的地理坐标和物理地址显示在地图下方。这使应用程序感觉更具吸引力和交互性。

注意:对于地名缩写,Mapcode使用ISO 3166标准。这意味着它可能无法解释一些常用的缩写。例如,如果您想为路易斯安那州的地址生成地图代码并输入LA,地图将跳转到加利福尼亚州的洛杉矶(而不是路易斯安那州)。

您可以通过在它们之前加US-以避免与美国邮政缩写混淆。在这个路易斯安那州的例子中,你会进入US-LA

要了解有关Mapcode如何使用此标准的更多信息,请查看“ 地区和标准代码”参考页

尽管应用程序在地图上显示位置的方式有所改进,但该应用程序仍未完全正常运行。在生成mapcode之前,您需要采取的最后一步是编辑db.php文件以允许应用程序访问您的数据库。

第9步 - 添加数据库凭据和测试地图代码生成

回想一下,此应用程序将在表单中输入的每个地址 - 以及其纬度,经度和地图代码 - 存储在您在步骤2中创建的数据库中。这可以通过db.php文件中的代码实现,该代码存储您的数据库凭据并允许应用程序访问其中的locations表。

作为启用地图代码生成功能的最后一步,打开db.php文件进行编辑:

nano /var/www/html/digiaddress/db.php

在此文件的顶部附近,找到以$pass。开头的行。此行提交您的MySQL登录凭据,以允许应用程序访问您的数据库。将your_password替换为为您的root用户的MySQL密码:

. . .
        $username = "root";
        $pass = "your_password";
. . .

这是您需要进行的最后一次更改,以便从物理地址生成地图代码。保存并关闭该文件,然后再次在浏览器中刷新应用程序。输入您选择的地址,然后单击“ 生成”按钮。输出看起来类似于:

在此阶段,您已完成申请,现在可以为世界上任何实际位置生成短数字地址。您可以随意尝试不同的地址,并注意您输入的地址不一定需要在美国境内。

您的最后一项任务是启用此应用程序的第二个功能:使用相应的地图代码从数据库中检索地址。

第10步 - 检索物理地址

现在您可以从给定的物理地址生成地图代码,最后一步是检索从地图代码派生的原始物理地址。为此,我们将开发一个PHP用户界面,如下所示:

该UI的代码在findaddress.php文件中可用。由于此文件中定义的UI与我们之前在步骤4中介绍的UI非常相似,因此我们不会过分关注其工作原理的所有细节。但是,我们将通过这三个文件来解释它们的功能。

要启用地址检索功能,您需要将Google API密钥添加到findaddress.php文件中,然后使用你喜欢的编辑器将其打开:

nano /var/www/html/digiaddress/findaddress.php

在文件底部附近,找到以<script async defer src=开头的行。它看起来像这样:

<script async defer src="https://maps.googleapis.com/maps/api/js?key=<YOUR KEY>"></script>

如前所述,使用Google API密钥替换<YOUR KEY>,然后保存文件。然而,在关闭之前,让我们快速浏览一下这些文件如何协同工作。

当用户提交表单时,它会触发一个submit事件,并且事件监听器会调用该fetchadd函数:

. . .
<form ng-submit="fetchadd()" class="custom-form">
. . .

fetchadd函数使用POST请求发送数字地址至fetchaddress.php

. . .
$http({
    method : 'POST',
    url : 'fetchaddress.php',
    data : {digiaddress: $scope.digiaddress}
}).then(function(response){
. . .

如果POST成功,则该函数返回JSON响应。以下行解析此响应:

. . .
var jsonlatlng = JSON.parse(response.data.latlng);
. . .

下一行在地图上设置标记:

. . .
marker = new google.maps.Marker({
    position: new google.maps.LatLng(jsonlatlng.latitude, jsonlatlng.longitude),
        map: locationMap
});
. . .

以下打印地理坐标和物理地址:

. . .
geoCoordLabel = angular.element(document.querySelector('#geocoordinates'));
geoCoordLabel.html("Geo Coordinate: "+ jsonlatlng.latitude +","+ jsonlatlng.longitude);

geoAddressLabel = angular.element(document.querySelector('#geoaddress'));
geoAddressLabel.html("Geo Address: " + jsonlatlng.house +","+ jsonlatlng.town +","+ jsonlatlng.street +","+ jsonlatlng.state + " " + jsonlatlng.zip );
. . .

访问以下链接,在浏览器中访问此应用程序:

http://your_server_ip/digiaddress/findaddress.php

通过输入您之前获得的地图代码进行测试。下图显示了典型输出:

这样,您的应用程序就完成了。您现在可以为世界上的任何位置创建唯一的地图代码,然后使用该地图代码检索位置的物理地址。

结论

在本教程中,您使用Google Maps API固定位置并获取其经度和纬度信息。此信息用于使用Mapcode API生成唯一且短的数字地址。地图代码有许多实际用例,从紧急服务到考古调查。Stichting Mapcode Foundation列出了几个这样的用例。

更多Ubuntu教程请前往腾讯云+社区学习更多知识。


参考文献:《How to Generate a Short and Unique Digital Address for Any Location Using AngularJS and PHP》

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小狼的世界

Silverlight学习笔记:资源的位置

    在 Web 项目中,我们免不了使用一些诸如图片、音频、视频、字体之类的在我们的程序中非可执行的数据文件,习惯称之为资源文件。在Silverlight中,...

702
来自专栏IT派

静态站点生成器:makesite.py

通过在Python中编写自己的简单、轻量级、无魔法的静态站点生成器,完全控制静态网站/博客生成。对的!重新发明轮子,伙计们!

1563
来自专栏小程序·云开发专栏

一次在微信小程序里跑 h5 页面的尝试

标题看起来有点唬人,在微信小程序里跑 h5 页面,不会又是说使用 web-view 组件来搞吧?确实,使用 web-view 组件可以达到跑 h5 页面的要求,...

2.1K2
来自专栏c#开发者

Vs.net 2008 sp1新特性之Dynamic Data Web Site

Vs.net 2008 sp1新特性之Dynamic Data Web Site 介绍 asp.net的动态数据,是一个web site开发框架,可让您很容...

3445
来自专栏零基础使用Django2.0.1打造在线教育网站

零基础使用Django2.0.1打造在线教育网站(二十六):xadmin的进阶开发

努力与运动兼备~~~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!

2642
来自专栏ml

C/C++-----------http协议发送字段,文件,单个和多张图片

           关于c/c++ 网络编程,无论在linux还是windows,要说到自由性,和安全性,socket无疑是比较好的!对于socket,因为它...

1.4K8
来自专栏转载gongluck的CSDN博客

超级强大的vim配置(vimplus)

最近在重新配置Vim,也在GitHub上找了三个star和fork数目很高的方案,在这里分享给大家: https://github.com/amix/vimr...

7.6K9
来自专栏机器学习和数学

[编程经验] Python中使用selenium进行动态爬虫

Hello,大家好!停更了这么久,中间发生了很多事情,我的心情也发生了很大的变化,看着每天在增长的粉丝,实在不想就这么放弃了,所以以后我会尽量保持在一周一篇的进...

1882
来自专栏葡萄城控件技术团队

带你走近AngularJS - 基本功能介绍

带你走近AngularJS系列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创...

21310
来自专栏阮一峰的网络日志

URL的井号

在我印象中,这是主流网站第一次将"#"大规模用于直接与用户交互的关键URL中。这表明井号(Hash)的作用正在被重新认识。本文根据HttpWatch的文章,整理...

1022

扫码关注云+社区

领取腾讯云代金券