paint-brush
使用 React 和 Mapbox 制作网络地图经过@rusanovn
1,042 讀數
1,042 讀數

使用 React 和 Mapbox 制作网络地图

经过 Nikita Rusanov13m2024/02/01
Read on Terminal Reader

太長; 讀書

我将说明如何将 Mapbox 与 React 结合使用,并解释这些技术如何协同工作。
featured image - 使用 React 和 Mapbox 制作网络地图
Nikita Rusanov HackerNoon profile picture
0-item
1-item

Web 应用程序极大地受益于地图的包含,为用户提供了有价值的基于位置的信息。地图改变了我们与世界的互动,从导航陌生的地方到发现附近的餐馆。因此,近年来,将地图集成到网站中变得越来越流行。然而,设计既实用又用户友好的地图可能会带来挑战,特别是对于那些缺乏该领域经验的人来说。在本文中,我将分享有关如何在浏览器中创建有效地图的有用技巧。

技术栈

我们来讨论一下技术。使用地图时,我们通常使用三层:

  • 呈现用户界面,其中包括按钮和表单。在我们的堆栈中,React 扮演着这个角色;

  • 渲染地图并启用用户交互。为此,我们使用 Mapbox;

  • 从服务器获取数据,例如有关标记或多边形的信息。为了检索数据,我们使用浏览器的内置获取功能。


让我们回顾一下每个项目,以便更好地了解我们在使用地图时的技术堆栈。

反应

标记和地图控件——这些是 React 组件。


反应库使您能够方便高效地使用页面元素。它是由 Facebook 开发供自己使用的,包括许多组件,例如按钮、表单和页面上的其他交互元素。例如,Facebook 创建了一个协调算法快速比较不同的状态。


由于更改页面上的元素对于浏览器来说是最昂贵的操作,因此必须尽可能高效地执行此操作。为了解决这个问题,Facebook 工程师开发了 React 库,它可以快速、直接地更改页面上的元素。除了在页面上提供快速状态更改之外,React 还允许我们以声明方式执行此操作,而无需直接使用 DOM 元素。相反,我们通常使用抽象JSX ,看起来像 HTML。让我们考虑一个例子:

 // It's our state. Is the user our friend or not? // false by default const [isFriend, setIsFriend] = useState(false) // Depending on the state, we show the text on the button const buttonText = isFriend ? 'Your my Friend' : 'Add as Friend' // There is JSX, syntax for UI // In this case, we display a button, when clicked, we change the state return ( <button onClick={() => setIsFriend(true)}>{buttonText}</button> )


可以将组件与普通 DOM 元素(例如表单、按钮和输入)嵌套在层次结构的底部。通过组装这些简单的元素,我们可以创建更复杂的元素,例如完整的表单:

 const Form = () => ( <form> <input name="Email"/> <input name="Password"/> </form> ) const App = () => ( <main> <h1>My form!</h1> <Form /> </main> )


React 如何在地图上下文中帮助我们?由于页面上的地图是交互式的,类似于按钮或表单,我们的目标是通过点击地图等事件来优化其渲染和交互。 React 可以帮助实现这种优化。以下是其工作原理的示例:

 // Use React to render the map with different event handlers // and render markers return ( <BaseMap onInitMap={() => console.log('I am alive!')} onClickMap={() => console.log('Click!')} onDestroyMap={() => console.log('Oh no!')} > <ClustersMarkers /> <PostsMarkers /> <ListingsMarkers /> </BaseMap> )


使用 React 时,必须记住,它允许高效地操作页面上的元素、快速更改以及通过事件与它们交互。这是通过类似于 HTML 的抽象来实现的,从而可以轻松地从简单的组件创建复杂的组件。

地图盒

Mapbox 允许我们在地图上显示我们自己的数据。


现在,让我们讨论地图本身。创建和使用地图可能具有挑战性,只有少数产品公司可以从头开始设计地图。通常,大多数人依赖带有经过尝试和测试的用户友好 API 的预制库。


有许多动态地图提供商可供使用,包括 Google 地图、Leaflet、Bing 地图、Mapbox 等。不过,我们将重点关注地图盒由于其广泛的功能和公平的定价政策。例如,谷歌地图收费700 美元每月 10 万次地图浏览,而 Mapbox 只收费250 美元。此外,Mapbox 每月提供最多 5 万次地图视图的免费访问。


Mapbox 优惠地图盒工作室,一种经常与地图 Photoshop 相比较的工具。使用此工具,设计人员可以创建自定义样式、隐藏不必要的地图元素以及删除房屋显示。其好处之一是能够提高知名度兴趣点。另一个优点是有机会尝试使用企业颜色的地图样式。然而,必须考虑用户体验并保持熟悉的调色板,包括绿草和蓝色的水。 Mapbox Studio 最好的部分是,它通过消除开发和传输需求的需要来简化地图设计过程,最终降低使用地图的成本。


此外,Mapbox 还提供地理编码工具它可以将地址转换为坐标,反之亦然,从而简化了在地图上定位点的过程。然而,该工具的全球覆盖可能不够,导致某些项目遇到困难。 Mapbox 收集数据众多来源,例如政府、OpenStreetMap 等开放数据计划以及私营公司。


Examples of using Mapbox.

Mapbox 提供了多种实际例子, 虽然文档可以从一些改进中受益。例如,当合并标记功能时,示例可以比文档提供更多信息。此外,合并多个组件时可能会出现挑战,例如标记集群、自定义数据加载、标准化和标记状态更改。


让我们回顾一下 Mapbox 地图。什么是地图盒API做?


  • 它在页面上的 HTML 元素中初始化地图;

  • 它加载并渲染构成地图的图像;

  • 它使用 GeoJson 作为输入数据绘制附加元素,例如标记;

  • 它生成可以处理的事件,例如单击或缩放更改。


让我们仔细看看其中的每一项。


The map is divided into tiles (512x512).

Mapbox 专注于使用图块进行地图渲染。图块是构成较大地图的小方形图像。图块的默认大小为 512x512 像素,可以是矢量或光栅向量图块用于显示道路、建筑物、兴趣点 (PoI) 等。它们可以动态设计样式,重量轻,并且可以与地图进行流畅的交互。光栅另一方面,图块用于显示卫星图像。


如您所知,Mapbox Studio 允许我们选择要包含在地图图块中的特定数据。然后将这些瓷砖放置在帆布,它是浏览器中的特殊 DOM 元素,用于在 Web 上显示图像和其他图形元素。为了让您了解一下,它类似于 Google 文档中文档在画布上的显示方式。

 <canvas width="100" height="100" />


Mapbox 处理图块的加载、插入和更新。我们所要做的就是指定希望地图显示的位置以及初始条件,例如缩放级别或地图坐标。要使用 Mapbox,您需要访问令牌,这是可以在您的 Mapbox 帐户中找到的唯一密钥。要设置基本地图,这里有一个简单的示例,但有关更多信息,请查看关联假如:

 mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; const map = new mapboxgl.Map({ container: 'map', // we can use an Id or an element style: 'mapbox://styles/mapbox/streets-v11', // URL for styles center: [-74.5, 40], // initial coordinates [lng, lat] zoom: 9, // initial zoom });


之后,我们将在页面上的 id 为“map”的元素中获取地图。


显示没有附加内容的地图。


为了在地图上向用户提供更多信息,我们经常显示某个机构的位置或特定区域的边界。为了实现这一点,我们使用一种特定的数据格式,称为地理JSON并指导 Mapbox 如何展示这些数据。


GeoJSON 是在地图上存储地理结构的标准格式。它可以存储描述地理对象的各种原始类型,例如地址、位置、街道、高速公路、边界、国家、州以及这些的组合,称为多部分。 GeoJSON 于 2008 年推出,表示方式如下:

 { "type": "Feature", // also can be FeatureCollection, it's collection of Feature "geometry": { "type": "Point", // also can be LineString, Polygon, MultiPolygon "coordinates": [125.6, 10.1] // for other types you can use Array with coordinates }, "properties": { // it's metadata, we can you that to show something on the map "name": "Dinagat Islands" } }


我们来谈谈Mapbox中使用的坐标系。默认情况下,Mapbox 使用每股收益 3857 ,这是一种将地球投影到平面上以使用瓷砖的方法。这称为 Web Mercator,是在线地图的标准。但是,在处理有关标记和多边形的数据时,它使用不同的坐标系,称为EPSG 4326 。该系统依靠纬度和经度来描述地球椭球体上的坐标。 Mapbox 和其他地图提供商会自动将坐标从 EPSG 4326 转换为 EPSG 3857。如果我们需要使用不同的预测,我们可以使用map.setProjection方法。


现在,我们将讨论如何在地图上显示 GeoJSON。 Mapbox 提供了两个对我们很有帮助的实体:

  • 来源— 这是数据种子。我们可以使用 GeoJSON 数据创建一个新源并对其进行配置,例如,为要素集合中的每个要素生成 ID。

  • — 这是数据表示。我们可以通过不同的方式显示源中的数据,例如显示边界。


要在地图上显示多边形或标记,我们必须从服务器检索 GeoJson 格式的数据。然后,我们创建一个源,将数据输入其中,并将其连接到所需的层。

 const geoJsonFeature = { 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [ [-67.13734, 45.13745], [-66.96466, 44.8097], [-68.03252, 44.3252], [-67.13734, 45.13745] ] } } // Create source with our data map.addSource('ourSource', { 'type': 'geojson', 'data': geoJsonFeature }); // Add layer for background map.addLayer({ 'id': 'background', 'type': 'fill', 'source': 'ourSource', // название нашего source 'layout': {}, 'paint': { 'fill-color': '#0080ff', 'fill-opacity': 0.5 } }); // Add layer for border map.addLayer({ 'id': 'border', 'type': 'line', 'source': 'ourSource', 'layout': {}, 'paint': { 'line-color': '#000', 'line-width': 3 } });


运行这段代码后,我们得到结果:

我们可以在 Mapbox 上显示我们自己的数据。


要了解有关此主题的更多信息,您可以参考文档由 Mapbox 提供。在本教程中,我们介绍了初始化地图和呈现数据的过程。接下来,我们将探讨如何处理单击、拖动和缩放等事件。例如,我们可以使用 Mapbox 来监视事件并在用户在地图上移动光标时在控制台中显示坐标。为了实现这一点,我们只需调用我们想要的事件类型的on方法,类似于我们使用 DOM 元素的方式。

 map.on('mousemove', (e) => { console.log(JSON.stringify(e.point)); }); // Result: {"x":330,"y":49}


总而言之,我们需要记住什么? Mapbox 允许我们显示地图、在其上绘制数据以及处理地图事件。同时,Mapbox 负责加载和显示图像(图块)。

拿来

Fetch 允许我们加载地图数据。


一句话关于拿来。我们已经了解了如何在地图上渲染数据,但首先,我们必须从服务器检索数据。当我们在后台动态地向服务器请求数据而不重新加载页面时,我们称这种方式为阿贾克斯(“同步JavaScriptXML ”)。有很多工具可以从服务器异步加载数据,例如 Axios 或 XMLHttpRequest(原生)。


要记住什么?我们从服务器检索数据,有很多库用于此目的,但我们将使用 fetch。接下来,我们将了解在使用地图时如何具体执行此操作,因为存在细微差别。

反应+Mapbox

现在让我们看看上述技术如何协同工作。首先,我们将使用 fetch 检索用于显示多边形的数据。然后我们将声明地图的初始化,并在加载后将多边形添加到地图中。


您还可以在以下位置找到一个工作示例关联假如。

 const useFetch = () => { /* Our data { 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [-67.13734, 45.13745], [-68.03252, 44.3252], [-68.90478, 47.18479], [-67.13734, 45.13745], ] ] } } */ const [data, setData] = useState(null) useEffect(() => { fetch('https://our-api.com/polygon') .then(response => response.json()) .then(setData) .catch(e => { console.error(e) }) }, [setData]) return { data } } const BaseMap = () => { // Use the hook to fetch data const { data } = useFetch(GET_REGION); // Map instance const map = useRef(null); // DOM element const mapContainer = useRef(null); // Main logic - init the map and add the event useEffect(() => { if (map.current) { return; // initialize map only once } mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; map.current = new mapboxgl.Map({ container: mapContainer.current, style: 'mapbox://styles/mapbox/light-v10', // style URL (it's Mapbox's core style) center: [-68.137343, 45.137451], // starting position zoom: 5 // starting zoom }); // Handle event map.on('load', () => { const sourceId = 'source-region' // Add a data source containing GeoJSON data map.addSource(sourceId, { 'type': 'geojson', 'data': data.region // our data from Apollo }); // Add a new layer to visualize the polygon map.addLayer({ 'id': 'background', 'type': 'fill', 'source': sourceId, // reference the data source 'paint': { 'fill-color': '#0080ff', // blue color fill 'fill-opacity': 0.5 } }); // Add a black outline around the polygon map.addLayer({ 'id': 'outline', 'type': 'line', 'source': sourceId, 'paint': { 'line-color': '#000', 'line-width': 3 } }); }); }); return <div ref={mapContainer} />; } 


Mapbox 和 React 让您可以轻松地在网络上使用地图。

结论

我们研究了支撑我们未来架构的技术堆栈。在下面的文章中,我们将讨论帮助设计地图架构的原则,如何实现模块的最大低耦合和高内聚,以及如何维护和开发可扩展的地图系统。


非常感谢您的关注!祝你有美好的一天。