Esri-Leaflet - 在一定距离内搜索
Esri-Leaflet - Search within a distance
我需要使用存储在 ArcGIS Online 中的要素图层来设计应用程序。使用 geocoder/search,我需要能够输入地址和 select 距离(1 个街区、2 个街区等)。结果将显示新点、距离半径和半径内的所有点。我还想要 table 个结果。
我需要的与 DataMade 的 Derek Eder 创建的这个应用程序完全一样:https://carto-template.netlify.app/,只是我需要将数据存储在安全的 ArcGIS 图层中。谁能给我指出一个示例、教程等,其中包含与此应用程序类似的 esri-leaflet 实现?我花了五天时间尝试转换代码,但我觉得我无处可去。
这里是 link 到 guthub:https://github.com/datamade/searchable-map-template-carto
--------更新--------
Seth - 我可以显示图层;但是,将搜索点与图层连接的查询不起作用。我想我漏掉了一些东西,因为控制台错误显示为“需要令牌”。见下文:
const radius = 1610;
/**************************************************************************************************/
// ArcGIS Authoization
/**************************************************************************************************/
$("#loginModal").modal({ backdrop: 'static', keyboard: false });
// submit element of form
var submitBtn = document.getElementById('btnArcGISOnline');
// add event listener to form
submitBtn.addEventListener('click', addServicesFromServer);
// create map and set zoom level and center coordinates
var map = L.map('mapCanvas', {
}).setView([30.46258, -91.13171], 12);
// set basemap to Esri Streets
L.esri.basemapLayer('Streets').addTo(map);
var layerurl = 'secure/layer/URL';
var tokenUrl = 'https://www.arcgis.com/sharing/generateToken';
// function to make request to server
function serverAuth(server, username, password, callback) {
L.esri.post(server, {
username: username,
password: password,
f: 'json',
expiration: 86400,
client: 'referer',
referer: window.location.origin
}, callback);
}
// function to run when form submitted
function addServicesFromServer(e) {
// prevent page from refreshing
e.preventDefault();
// get values from form
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
// generate token from server and add service from callback function
serverAuth(tokenUrl, username, password, function (error, response) {
if (error) {
return;
}
// add layer to map
var featureLayer = L.esri.featureLayer({
url: layerurl,
opacity: 1,
token: response.token
});
featureLayer.addTo(map);
$("#loginModal").modal("hide");
}); // end serverAuth call
} // end addServicesFromServer call
// HARNESS GEOCODER RESULTS
let circle;
// GeoSearch
const search = L.esri.Geocoding.geosearch({
useMapBounds: false,
expanded: true,
collapseAfterResult: false
});
search.addTo(map);
search.on("results", (results) => {
if (results && results.latlng) {
if (circle) {
circle.remove();
}
circle = L.circle(results.latlng, { radius });
circle.addTo(map);
queryLayer(results.latlng);
}
});
// SET UP QUERY FUNCTION
function queryLayer(point) {
const query = L.esri.query({ url: layerurl }).nearby(point, radius);
query.run(function (error, featureCollection, response) {
if (error) {
console.log(error);
return;
}
console.log(featureCollection.features);
populateList(featureCollection.features);
});
}
// WRITE RESULTS INTO A LIST
function populateList(features) {
const list = document.getElementById("results-list");
let listItems = "";
features.forEach((feature) => {
listItems =
listItems +
`
<li>
Place: ${feature.properties?.Location} <br>
Lat: ${feature.properties?.Latitude} <br>
Lng: ${feature.properties?.Longitude} <br>
</li>
`;
list.innerHTML = listItems;
});
}
我尝试将令牌传递给下面粘贴的查询,但随后收到无效令牌错误。
var layerUrl_token = layerurl + "?token=" + response.token;
我也试过用turf.js,但是没成功。我知道 turf.js 使用 long/lat,但我什至无法获得正确的语法来从要素层中提取纬度和经度。
你想做的事情并不难。虽然有一些教程涉及您想要执行的不同 部分 ,但让我们将它们拼凑起来。我将使用 esri-leaflet-geocoder 作为我的搜索功能,因为它与 esri-leaflet 一致,而 IMO 是可用于传单的最佳地理编码器之一。
设置地址解析器
设置基本的传单地图后,让我们导入 esri-leaflet 和 esri-leaflet-geocoder,并创建一个地理编码器:
import L from "leaflet";
import * as EL from "esri-leaflet";
import * as ELG from "esri-leaflet-geocoder";
const search = ELG.geosearch({
useMapBounds: false,
expanded: true,
collapseAfterResult: false
});
search.addTo(map);
不要忘记将地理编码器 css 添加到您的 html,如 documentation example.
中所示
添加图层:
const layerurl = "YOUR_LAYER_URL";
const featureLayer = EL.featureLayer({ url: layerurl });
featureLayer.addTo(map);
如果您使用的是需要身份验证的层,则需要获取令牌并将其用作要素层中的选项之一,(featureLayer({ url: layerurl, token: token })
)。如果您不确定如何获取令牌,请发表评论,我可以为此添加一些代码,但是已经有一些不错的教程可以使用。
利用搜索结果
ELG.geosearch
附带一个您可以利用的 results
事件。当用户在地理搜索的自动完成下拉列表中选择一个结果时调用它。在那种情况下,我们可以获得用户选择的位置的位置数据。我们将地图居中(这实际上是 geosearch 的默认设置),绘制一个具有给定半径的圆,并执行查询(更多关于该层):
let circle;
search.on("results", (results) => {
if (results && results.latlng) {
if (circle) {
circle.remove();
}
circle = L.circle(results.latlng, { radius });
circle.addTo(map);
queryLayer(results.latlng);
}
});
查询图层
现在我们知道了用户从搜索中选择的位置 latlng
。我们可以创建一个 esri-leaflet query
,它可以以各种方式查询您的要素图层。我们将看到一个 nearby
查询,它将在图层中查询给定点半径内的任何要素:
function queryLayer(point) {
const query = EL.query({ url: layerurl }).nearby(point, radius);
query.run(function (error, featureCollection, response) {
if (error) {
console.log(error);
return;
}
populateList(featureCollection.features);
});
}
如果您正在查询经过身份验证的层,则需要向请求添加令牌。我相当确定这样做的方法是这样的:
function queryLayer(point) {
const query = EL.query({ url: layerurl })
.token(<your_token_here>)
.nearby(point, radius);
// ... same as above
}
您还可以 运行 直接从您的层进行查询:
featureLayer.query().nearby(point, radius)
我不太熟悉第二种方式,但您可以在这里阅读更多相关信息:Query a feature layer。
呈现到页面
一旦我们.run
查询,我们就可以访问featureCollection
形式的结果。我们可以遍历该 featureCollection 的特征并渲染一些 HTML:
function populateList(features) {
const list = document.getElementById("results-list");
let listItems = "";
features.forEach((feature) => {
listItems =
listItems +
`
<li>
Place: ${feature.properties?.Location} <br>
Lat: ${feature.properties?.Latitude} <br>
Lng: ${feature.properties?.Longitude} <br>
</li>
`;
list.innerHTML = listItems;
});
}
在这个特定示例中,我使用了我制作的点层,该点层通过 arcgis online 提供。此点层没有地址数据,因此 feature.properties
不包含任何地址信息。对于您的要素图层,图层的属性将在 feature.properties
中可用。因此,根据那里的内容,您可能想要使用 feature.properties?.address
或其他内容。最后一点只是一个示例,您可能会根据自己的目的进行很多不同的自定义。
Working Codesandbox
在此示例中尝试搜索人口稠密的地区。请注意,在此要素图层中有许多重叠位置,因此列表中的结果比地图上的标记看起来要多。
另请注意,我使用 esri-leaflet 和 esri-leaflet-geocoder 版本 2^ 发布此示例。这些在大约 1-2 个月前刚刚更新到版本 3,新版本需要在地理编码器和图层声明中使用 API 键,因此如果您想使用最新版本(推荐),您将需要添加它们。我使用版本 2,以免在沙箱中暴露 API 密钥(我有点讨厌新的 API 密钥要求。新的 arcgis developers documentation for esri-leaflet has some examples of that, but the official documentation has not yet been updated以匹配这些示例。
我需要使用存储在 ArcGIS Online 中的要素图层来设计应用程序。使用 geocoder/search,我需要能够输入地址和 select 距离(1 个街区、2 个街区等)。结果将显示新点、距离半径和半径内的所有点。我还想要 table 个结果。
我需要的与 DataMade 的 Derek Eder 创建的这个应用程序完全一样:https://carto-template.netlify.app/,只是我需要将数据存储在安全的 ArcGIS 图层中。谁能给我指出一个示例、教程等,其中包含与此应用程序类似的 esri-leaflet 实现?我花了五天时间尝试转换代码,但我觉得我无处可去。
这里是 link 到 guthub:https://github.com/datamade/searchable-map-template-carto
--------更新--------
Seth - 我可以显示图层;但是,将搜索点与图层连接的查询不起作用。我想我漏掉了一些东西,因为控制台错误显示为“需要令牌”。见下文:
const radius = 1610;
/**************************************************************************************************/
// ArcGIS Authoization
/**************************************************************************************************/
$("#loginModal").modal({ backdrop: 'static', keyboard: false });
// submit element of form
var submitBtn = document.getElementById('btnArcGISOnline');
// add event listener to form
submitBtn.addEventListener('click', addServicesFromServer);
// create map and set zoom level and center coordinates
var map = L.map('mapCanvas', {
}).setView([30.46258, -91.13171], 12);
// set basemap to Esri Streets
L.esri.basemapLayer('Streets').addTo(map);
var layerurl = 'secure/layer/URL';
var tokenUrl = 'https://www.arcgis.com/sharing/generateToken';
// function to make request to server
function serverAuth(server, username, password, callback) {
L.esri.post(server, {
username: username,
password: password,
f: 'json',
expiration: 86400,
client: 'referer',
referer: window.location.origin
}, callback);
}
// function to run when form submitted
function addServicesFromServer(e) {
// prevent page from refreshing
e.preventDefault();
// get values from form
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
// generate token from server and add service from callback function
serverAuth(tokenUrl, username, password, function (error, response) {
if (error) {
return;
}
// add layer to map
var featureLayer = L.esri.featureLayer({
url: layerurl,
opacity: 1,
token: response.token
});
featureLayer.addTo(map);
$("#loginModal").modal("hide");
}); // end serverAuth call
} // end addServicesFromServer call
// HARNESS GEOCODER RESULTS
let circle;
// GeoSearch
const search = L.esri.Geocoding.geosearch({
useMapBounds: false,
expanded: true,
collapseAfterResult: false
});
search.addTo(map);
search.on("results", (results) => {
if (results && results.latlng) {
if (circle) {
circle.remove();
}
circle = L.circle(results.latlng, { radius });
circle.addTo(map);
queryLayer(results.latlng);
}
});
// SET UP QUERY FUNCTION
function queryLayer(point) {
const query = L.esri.query({ url: layerurl }).nearby(point, radius);
query.run(function (error, featureCollection, response) {
if (error) {
console.log(error);
return;
}
console.log(featureCollection.features);
populateList(featureCollection.features);
});
}
// WRITE RESULTS INTO A LIST
function populateList(features) {
const list = document.getElementById("results-list");
let listItems = "";
features.forEach((feature) => {
listItems =
listItems +
`
<li>
Place: ${feature.properties?.Location} <br>
Lat: ${feature.properties?.Latitude} <br>
Lng: ${feature.properties?.Longitude} <br>
</li>
`;
list.innerHTML = listItems;
});
}
我尝试将令牌传递给下面粘贴的查询,但随后收到无效令牌错误。
var layerUrl_token = layerurl + "?token=" + response.token;
我也试过用turf.js,但是没成功。我知道 turf.js 使用 long/lat,但我什至无法获得正确的语法来从要素层中提取纬度和经度。
你想做的事情并不难。虽然有一些教程涉及您想要执行的不同 部分 ,但让我们将它们拼凑起来。我将使用 esri-leaflet-geocoder 作为我的搜索功能,因为它与 esri-leaflet 一致,而 IMO 是可用于传单的最佳地理编码器之一。
设置地址解析器
设置基本的传单地图后,让我们导入 esri-leaflet 和 esri-leaflet-geocoder,并创建一个地理编码器:
import L from "leaflet";
import * as EL from "esri-leaflet";
import * as ELG from "esri-leaflet-geocoder";
const search = ELG.geosearch({
useMapBounds: false,
expanded: true,
collapseAfterResult: false
});
search.addTo(map);
不要忘记将地理编码器 css 添加到您的 html,如 documentation example.
中所示添加图层:
const layerurl = "YOUR_LAYER_URL";
const featureLayer = EL.featureLayer({ url: layerurl });
featureLayer.addTo(map);
如果您使用的是需要身份验证的层,则需要获取令牌并将其用作要素层中的选项之一,(featureLayer({ url: layerurl, token: token })
)。如果您不确定如何获取令牌,请发表评论,我可以为此添加一些代码,但是已经有一些不错的教程可以使用。
利用搜索结果
ELG.geosearch
附带一个您可以利用的 results
事件。当用户在地理搜索的自动完成下拉列表中选择一个结果时调用它。在那种情况下,我们可以获得用户选择的位置的位置数据。我们将地图居中(这实际上是 geosearch 的默认设置),绘制一个具有给定半径的圆,并执行查询(更多关于该层):
let circle;
search.on("results", (results) => {
if (results && results.latlng) {
if (circle) {
circle.remove();
}
circle = L.circle(results.latlng, { radius });
circle.addTo(map);
queryLayer(results.latlng);
}
});
查询图层
现在我们知道了用户从搜索中选择的位置 latlng
。我们可以创建一个 esri-leaflet query
,它可以以各种方式查询您的要素图层。我们将看到一个 nearby
查询,它将在图层中查询给定点半径内的任何要素:
function queryLayer(point) {
const query = EL.query({ url: layerurl }).nearby(point, radius);
query.run(function (error, featureCollection, response) {
if (error) {
console.log(error);
return;
}
populateList(featureCollection.features);
});
}
如果您正在查询经过身份验证的层,则需要向请求添加令牌。我相当确定这样做的方法是这样的:
function queryLayer(point) {
const query = EL.query({ url: layerurl })
.token(<your_token_here>)
.nearby(point, radius);
// ... same as above
}
您还可以 运行 直接从您的层进行查询:
featureLayer.query().nearby(point, radius)
我不太熟悉第二种方式,但您可以在这里阅读更多相关信息:Query a feature layer。
呈现到页面
一旦我们.run
查询,我们就可以访问featureCollection
形式的结果。我们可以遍历该 featureCollection 的特征并渲染一些 HTML:
function populateList(features) {
const list = document.getElementById("results-list");
let listItems = "";
features.forEach((feature) => {
listItems =
listItems +
`
<li>
Place: ${feature.properties?.Location} <br>
Lat: ${feature.properties?.Latitude} <br>
Lng: ${feature.properties?.Longitude} <br>
</li>
`;
list.innerHTML = listItems;
});
}
在这个特定示例中,我使用了我制作的点层,该点层通过 arcgis online 提供。此点层没有地址数据,因此 feature.properties
不包含任何地址信息。对于您的要素图层,图层的属性将在 feature.properties
中可用。因此,根据那里的内容,您可能想要使用 feature.properties?.address
或其他内容。最后一点只是一个示例,您可能会根据自己的目的进行很多不同的自定义。
Working Codesandbox
在此示例中尝试搜索人口稠密的地区。请注意,在此要素图层中有许多重叠位置,因此列表中的结果比地图上的标记看起来要多。
另请注意,我使用 esri-leaflet 和 esri-leaflet-geocoder 版本 2^ 发布此示例。这些在大约 1-2 个月前刚刚更新到版本 3,新版本需要在地理编码器和图层声明中使用 API 键,因此如果您想使用最新版本(推荐),您将需要添加它们。我使用版本 2,以免在沙箱中暴露 API 密钥(我有点讨厌新的 API 密钥要求。新的 arcgis developers documentation for esri-leaflet has some examples of that, but the official documentation has not yet been updated以匹配这些示例。