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

使用 Wijmo Bubble Maps 映射 JSON 数据集

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2021年9月17日

CPOL

9分钟阅读

viewsIcon

5119

如何使用 Wijmo、GrapeCity 提供的 100 多个动态 JavaScript UI 组件及其气泡地图功能,快速构建带有有用覆盖数据的地图

几个世纪以来,人类一直在地图上叠加信息,从最佳的军队营地位置到城市里最好的酒吧。如今,这些地图通常是数字化的、基于网络的,显示着从天气信息到病毒热点,再到城市里最好的酒吧等各种信息。今天,我们将探索如何使用 Wijmo、GrapeCity 提供的 100 多个动态 JavaScript UI 组件及其气泡地图功能,快速构建带有有用覆盖数据的地图。

我们的教程将使用 Wijmo React 库,但也提供 Vue、Angular 和纯 JavaScript (JS) 库。首先,我们将设置一个入门级 React 应用,然后使用来自 API 的静态 GeoJSON 创建一个 Wijmo 地图。

最后,我们将介绍如何使用来自实时 API 的数据添加工具提示。我们的应用程序将绘制加拿大谷物电梯的固定位置,然后绘制实时 COVID-19 信息,但重要的是要认识到可能性是无限的。

本教程使用 Wijmo 的 30 天免费试用版。要在生产环境中使用这些库,您需要 购买 Wijmo 许可证密钥 并将其提供给您的安装。要遵循本教程,您应该熟悉 npm 和 React,尽管我们会展示构建我们简单应用程序的所有步骤。

设置 React 页面

我们将首先使用 Create React App 来设置一个骨架 React 应用。

npx create-react-app wijmo-geomaps

接下来,我们将切换到 Create React App 设置的目录。

cd wijmo-geomaps

从这里,我们安装 Wijmo 和用作开发服务器的 serve 包。

npm install @grapecity/wijmo.react.all # Installs all wijmo packages

npm install serve # Simple dev server

npm install bootstrap react-bootstrap # Bootstrap

虽然这是您从 npm 安装 Wijmo 的方式,但我们下载了 Wijmo 开发包 并使用 npm ln 将下载文件夹链接到我们的 node_modules 文件夹。然后我们可以从那里导入文件。

现在我们构建我们的骨架应用程序。

npm run build

最后,我们可以在浏览器中查看我们的入门级 React 应用程序。

serve -s build

上面的命令会在您的本地计算机上一个免费端口上提供应用程序。在浏览器中查看 https://:5000 以查看应用程序的 React 徽标。

现在我们已经创建了入门级应用程序并安装了 Wijmo,我们可以使用 Wijmo 气泡地图来构建地图。

使用静态 GeoJSON 创建 Wijmo 地图

让我们开始向我们的应用程序添加内容。首先,更改目录到您的 React 应用程序的源目录。

cd src

现在,在您喜欢的文本编辑器中打开 App.js 文件。删除除最后一行之外的所有行。

export default App;

我们将重写 App 类以使用 Wijmo 气泡地图,因此请按以下方式设置您的 App.js 文件。

import 'bootstrap/dist/css/bootstrap.min.css';

import * as React from ‘react’;

class App extends React.Component
     {
         constructor(props)
         {
             super(props); 
         }

         render()
         {
             return(
                 <div>Hello, world!</div>
             );
         }
}

export default App;

当您重新加载页面时,您应该会看到“Hello, World”文本。

现在让我们开始制作一个基本的静态地图。添加以下 JS 和 CSS 导入,以便您可以使用 Wijmo 的库。

/* Wijmo */
import { FlexMap, GeoMapLayer, ScatterMapLayer } from '@grapecity/wijmo.react.chart.map'; 
import { Rect } from "@grapecity/wijmo";
import '@grapecity/wijmo.styles/wijmo.css';

现在我们用一个容器替换我们的占位符来容纳我们的 Wijmo 地图。我们使用 React Bootstrap 的 container-fluid 类来确保地图看起来不错。返回以下 JavaScript XML (JSX)。

<div className="container-fluid">
</div>

我们可以添加 Wijmo 地图组件了。Wijmo 地图使用一系列图层,您可以添加任意数量的图层,在 JSX 中稍后声明的图层将覆盖之前的图层。

让我们在刚刚创建的地图中声明地图。

<FlexMap>
</FlexMap>

您可以为 FlexMap 添加许多属性,包括 header 来设置地图标题,以及 tooltipContent,它可以传递一个函数来返回一个 string,该字符串将在地图点处的工具提示中显示。现在我们使用 header 属性,稍后设置 tooltipContent 属性。

我们的地图标题指的是我们稍后将添加的谷物电梯数据源。

<FlexMap header="Grain Elevators in Canada">
</FlexMap>

此数据集可通过公共 REST API 获取,该 API 返回 Wijmo 地图使用的格式 GeoJSON。

现在,让我们向我们的应用程序添加一张加拿大地图。我们可以将此轮廓添加为 FlexMap 内的 GeoMapLayer

<FlexMap header="Grain Elevators in Canada">

    <GeoMapLayer
url="[https://raw.githubusercontent.com/johan/world.geo.json/master/countries/CAN.geo.json]
     (https://raw.githubusercontent.com/johan/world.geo.json/master/countries/CAN.geo.json)" 
style={{ fill: "transparent", stroke: "black" }}
/>
</FlexMap>

我们将 GeoJSON 数据作为 URI 提供给我们的 GeoMapLayer,并添加一些最小的 CSS 样式。当您重建应用程序并重新加载时,您应该会在地图上看到加拿大的轮廓。

轮廓在那里,但看起来很小,因为地图没有足够放大。我们将使用回调来修复这个问题。Wijmo 气泡地图支持 itemsSourceChanged 事件,该事件在地图的 GeoJSON 源更改时触发。在我们的例子中,这只发生一次,当地图加载时,所以我们将处理该事件时放大。为此,请在构造函数中添加以下行。

this.canadaBB = new Rect(-80, 30, 80, 80);

这使用了我们之前导入的 Rect 类来定义一个聚焦于加拿大轮廓的边界矩形。矩形的左上角将在 (-80, 30) 处,我们将两侧的长度都设置为 80 个单位。

接下来,我们为 itemsSourceChanged 事件添加一个处理程序方法。

    itemsSourceChanged(layer)  
    {  
        layer.map.zoomTo(this.canadaBB);  
    }

此处理程序将我们的侦听器传入图层,并在 GeoJSON 数据加载时调用。它使用该图层获取地图,然后使用我们之前定义的边界框放大加拿大轮廓。不要忘记在构造函数中绑定此处理程序,以便在将其传递给 GeoMapLayer 时能够正常工作。

this.itemsSourceChanged = this.itemsSourceChanged.bind(this);

最后,我们在 GeoMapLayer 中添加一个属性来设置我们图层的处理程序。

itemsSourceChanged={this.itemsSourceChanged}

构建您的应用程序并重新加载。由于处理程序在 GeoJSON 数据加载时放大,加拿大现在应该看起来更大了。

现在我们有了加拿大的轮廓,我们可以添加加拿大政府的 GeoJSON 数据集作为第二个 GeoMapLayer 来显示加拿大谷物电梯的位置。访问 加拿大政府网站,点击“GeoJSON”按钮,然后复制该页面的 URL。我们需要将该 URL 传递给我们的 GeoMapLayer

我们还将使用橙色样式化这些位置,以使其脱颖而出。总而言之,我们完整的 Flexmap 如下所示。

   <FlexMap header=”Grain Elevators in Canada”>  
            <GeoMapLayer  
url="[https://raw.githubusercontent.com/johan/world.geo.json/master/countries/CAN.geo.json]
     (https://raw.githubusercontent.com/johan/world.geo.json/master/countries/CAN.geo.json)"  
style={{ fill: "transparent", stroke: "black" }}  
/>  
<GeoMapLayer
url="[https://www.agr.gc.ca/atlas/data_donnees/agr/cgcElevators/geoJSON/cgcElevators2018.geojson]
     (https://www.agr.gc.ca/atlas/data_donnees/agr/cgcElevators/geoJSON/cgcElevators2018.geojson)"  
style={{ fill: "transparent", stroke: "orange" }}  
/>  
      </FlexMap>

重建并重新加载。您现在应该可以看到 2018 年加拿大谷物电梯位置周围的橙色轮廓。

就这样,我们创建了一个显示可识别的背景和 API 数据的地图。放大后,您还可以看到加拿大大部分谷物电梯都位于草原省份,如果您放大。

当然,您可以通过添加更多图层和附加数据来轻松地使设计更加复杂。您只需提供指向 GeoJSON 资源的 URL,例如本地文件或 API,然后该信息就会显示在您的地图上。

添加实时数据

通过添加更多组件和一些方法,我们可以轻松地包含来自实时 API 的数据。我们使用 一个 API 来提供有关加拿大 COVID-19 情况的数据。

我们将把这些实时数据转换为热图可视化,显示前一天每个省份的报告病例数。我们从以下 URI 获取数据:https://api.opencovid.ca/timeseries?stat=cases&loc=prov&after={prev-DD-MM-YYYY}&before={cur-DD-MM-YYYY}

我们选择获取每个省的总病例数。占位符表示我们通过为 after 参数传递前一天的日期,为 before 参数传递当天的日期来获取前一天的数据。

让我们先在构造函数中添加一个日期格式化对象。

this.dtFormatter = new Intl.DateTimeFormat("en-CA",
    {
        year: "numeric",
        month: "2-digit",
        day: "2-digit"
    }
);

这会创建一个对象来获取日期的各个部分。这些参数确保我们将获得四位数的年份和两位数的月份和日期值,这正是我们创建要传递给 API 的日期所需要的。让我们为类添加一个方法,该方法将昨天和今天的日期转换为正确的格式的字符串。

getDates()
{
    return {
        "today": this.dtFormatter.formatToParts(new Date()).reverse().map
                 (part => part.value).join(""),
        "yesterday": this.dtFormatter.formatToParts((new Date()).setDate
         ((new Date()).getDate() - 1)).reverse().map(part => part.value).join("")  
    };
}

此方法返回一个包含两个键的对象,一个用于今天,一个用于昨天。代码如下:获取昨天的日期或今天的日期,将其传递给格式化程序以返回年份、月份和日期作为整数,将列表反转为 DD-MM-YYYY 的顺序,提取列表中每个日期时间对象的数字,并将它们连接在一起。

幸运的是,日期格式化程序为我们插入了连字符,这正是 OpenCOVID API 所期望的。如果将 getDates 的输出记录到控制台,您应该会看到类似以下的 string

现在我们有了日期,我们可以添加另一个方法,该方法向 OpenCOVID API 发送请求并将响应解析为我们的地图可以使用的数据。

async getData(dates)
{ 
     const url = "https://api.opencovid.ca/timeseries?stat=cases&loc=prov&after=" + 
                  dates.yesterday + "&before=" + dates.today;
     const resp = await fetch(url);
     
     if (!resp.ok)
     {
         throw new Error(resp.statusText);
     }

     const json = await resp.json();

     return json;

}

getData 接受 getDates 返回的日期对象,并使用它来构造 URL。然后我们等待服务器的响应,接收响应后将其转换为 JSON,然后返回转换后的响应。如果响应无效,我们也会抛出错误。

获取数据的最后一步是在生命周期方法 componentDidMount 中调用 getData

async componentDidMount()
{
    const dates = this.getDates();
    
    try
    {
        let apiDat = await this.getData(dates);
        this.setState({
            covidData:apiDat
        });
    }
    catch (e)
    {
        console.log("componentDidMount: error occurred\nError: %o", e);
    }
}

由于我们正在获取前一天的 COVID 数据,因此我们的应用程序仅在组件首次加载时调用此方法。如果您记录数据,您会看到它具有以下结构。

为了简单起见,我们将仅显示累计病例数。

为了显示数据,首先在构造函数中创建一个隐藏的“数据映射”,将病例数映射到省份。

this.state = {
    dataMap: new Map(),
    loadedData: false
};

我们添加一个布尔变量来确保我们的热图仅在数据加载后渲染。

接下来,让我们回到 componentDidMount 并添加一些代码,将每个省份的累计病例数与该省份关联起来。

apiDat.cases.forEach(report => {
    this.dataMap.set(report.province, report.cumulative_cases);
});
this.setState({
    covidData: apiDat,
    loadedData: true
});

一旦我们将数据映射到省份,我们就告诉 React 使用新的 COVID 数据重新渲染我们的页面,并设置一个布尔值允许我们的热图图层显示。接下来,我们添加一个方法,在我们的热图图层请求数据时,该方法从我们的隐藏映射中获取数据。

必要时,此方法还会将省份名称从隐藏数据映射中使用的较长名称转换为 COVID-19 数据集中使用的缩写。我们还添加了一个方法,该方法反转病例百分比,以便病例数较高的省份颜色更深。

binding(o)
{
    let nameConv = {
        // Format: GeoJSON
        name : COVID data name
        "Newfoundland and Labrador": "NL",
        "British Columbia": "BC",
        "Northwest Territories": "NWT",
        "Prince Edward Island": "PEI",
        "Yukon Territory": "Yukon"
    };
    var dataMapName = o.properties.name;
    
    if (o.properties.name in nameConv)
    {
        dataMapName = nameConv[o.properties.name];
    }

    return this.state.dataMap.get(dataMapName);
}

scale(v)
{
    return 1 - v;
}

让我们还在构造函数中绑定这些新方法,以确保它们作为回调正常工作。

this.binding = this.binding.bind(this);
this.scale = this.scale.bind(this);

现在我们使用绑定方法添加一个 ColorScale 元素。让我们先将其与其他的 Wijmo React 图表元素一起导入。我们还将导入一些调色板来显示我们的热图,以及一个颜色比例尺来告知用户每种颜色的含义。

import { FlexMap, GeoMapLayer, <u>ColorScale</u> } from '@grapecity/wijmo.react.chart.map';
import { Palettes } from "@grapecity/wijmo.chart";
import { FlexChartLegend } from "@grapecity/wijmo.react.chart";

我们还将 GeoMapLayer 从单个元素更改为带有开始和结束标签的元素,以包含 ColorScale,并且我们将在图层之后添加一个 FlexChartLegend 以在地图左侧显示比例尺。

<GeoMapLayer
url="https://raw.githubusercontent.com/codeforamerica/click_that_hood/
     master/public/data/canada.geojson" 
    style={{ fill: "transparent", stroke: "black" }}
    itemsSourceChanged={this.itemsSourceChanged} 
    >

    {this.state.loadedData &&
        <ColorScale
            colors={Palettes.Diverging.RdYlBu}
            binding={this.binding}
            scale={this.scale}
        />
}
</GeoMapLayer>
<FlexChartLegend position="left"></FlexChartLegend>

我们使用布尔值来确保颜色比例尺仅在我们加载 COVID 数据后显示。地图将在受影响更严重的区域显示更红的颜色。

现在您有了一个显示来自 API 的实时数据的地图。

后续步骤

使用 Wijmo 气泡地图和标准的 GeoJSON 格式创建地图非常容易。您可以快速包含您的静态 JSON 文件或将地图指向 URL。

当您的静态底图准备就绪后,Wijmo 的组件和 JavaScript 钩子可以轻松地以自定义格式显示数据或显示实时数据。它们支持 React、Vue、Angular 或纯 JavaScript,您可以从 npm 安装包或下载完整的开发包、源 TypeScript、尝试示例等等。

既然您已经了解了如何轻松地将 Wijmo 气泡地图与公开可用的数据分层,您就可以在您的应用程序或网站中添加有用的地图:君主蝶栖息地改善、佛蒙特州的无线下载速度、弗吉尼亚州的社区游泳池、学区收入、水位,或者,是的,城市酒吧。

要开始创建下一个地图,或在您的应用程序中添加一百多种用户界面组件之一,请 探索 Wijmo

历史

  • 2021 年 9 月 17 日:初始版本
© . All rights reserved.