我的交通地理空间分析 MapInfo 作业

我的交通地理空间分析 MapInfo 作业

之前上课的时候老师讲了 MapInfo 中的面编辑操作,然后布置了一个作业,让我们在今年国庆节期间完成,回来上交。

因为面操作比较简单,所以关于面操作教程的内容我打算放到后面写;先讲讲这个作业怎么写。所以在本文中,我将演示我是如何完成我的 MapInfo 作业的。

这个文件里面会用到一些简单的面操作。不用担心,这些简单的操作在前面几期上课笔记里面我都有写到过——如果遇到不知道怎么操作的情形,直接去翻看我之前的文档就好了。

作业概述

作业要求是选择一个自己喜欢的题材,做一个和交通领域相关的 MapInfo 地理项目。作业提交只提交图层。

正好,我之前在 GtiHub 上面找到过这个项目:newzealandpaul / Maritime-Pirate-Attacks

项目描述:

This dataset contains information from more than 7,500 maritime pirate attacks that took place between January 1993 and December 2020, as well as country indicator data for the same time period.

简单的来说,这个项目是一个 1993 年到 2020 年海盗袭击事件的数据库。该数据库不仅记录了各种海盗袭击事件的细节,而且包含了经纬度数据。在项目 README 文件里面有下面的示例图:

image

我们可以利用这个数据集来做一个马六甲海峡的航线地图 & 海盗袭击事件的地图。

数据的收集

海盗袭击事件的数据集链接已经放在上面了。我们现在再去找一下全球航线的数据,用来标注马六甲地区的航线;再找一下全球航区的地图,用来标注马六甲航区的位置。

全球航线数据在同一个项目作者的 GitHub 主页里面有:newzealandpaul / Shipping-Lanes

如果电脑安装过 Git,可以直接用下面的命令下载数据(使用国内 GitHub 镜像):

git clone https://hub.njuu.cf/newzealandpaul/Maritime-Pirate-Attacks
git clone https://hub.njuu.cf/newzealandpaul/Shipping-Lanes.git

世界海区划分图可以在 Marineregions.org 找到。下载的时候网站会让你填写真实姓名和单位名称,因为网站要追踪数据都用于了哪些学术项目。这个填写自己的学校就好了。

地图绘制

世界地图底图的导入

首先我们先找到一张世界地图。这个世界地图我是在 国家标准地图服务网站 下载的 .jpg 格式的文件。

image

我们在 MapInfo 里面导入这个文件(Raster 格式)。选择地图配准。第一次的时候我选择了这几个经纬度的交叉点作为地图配准的控制点:

控制点 经度 纬度
Pt 1 -15 75
Pt 2 -15 -75
Pt 3 -45 -75
Pt 4 -45 75

然后我们导入之前下载好的世界航线图层。因为我们的航线图层是标准的地图 Shapefile 格式,所以这个文件类型也要选择为 ESRI Shapefile:

image

咦,奇怪,怎么轮船都开到天上去了?

仔细思考一下,认为导致这种问题的原因是四个控制点都在西半球,所以 MapInfo 软件自动认为这个地图是一个西半球的地图文件。

我们重新设置控制点:

控制点 经度 纬度
Pt 1 -15 75
Pt 2 -15 -75
Pt 3 -45 -75
Pt 4 -45 75
Pt 5 -150 75
Pt 6 150 75
Pt 7 150 -75
Pt 8 -150 -75

好家伙,这下地图直接不显示了。

image

不知道导致这种问题的原因是什么,在网上没有找到关于 MapInfo 配准世界地图的相关资料。

猜想:这个地图文件使用的不是标准的 WGS84 国际标准坐标系,而是某种国标的或者别的什么坐标系统。但是一方面,我们不知道这个地图究竟用的是什么坐标系统;另一方面,MapInfo 对坐标系的支持挺奇怪的。如下图:

image

我找了半天,只找到这几个国标的坐标系。

我不知道这是否意味着软件对中国坐标系的标准还在沿用 1980 年的老国标?我也没有找到我们熟悉的 CGCS 2000 大地坐标系。

所以我们换一种思路: 我们去找矢量地图格式的世界地图,这样就不需要对地图进行配准了。

刚好,在 中国科学院资源环境科学与数据中心 就有一套世界地图的矢量图层 Shapefile 文件。我们直接访问网站。

  1. 注册一个中科院环境资源数据中心账号并登录
  2. 在网站首页的左侧的 【数据集(库)目录】下找到【全球100万基础地理数据】
  3. 点进去,找到 全球国家行政边界数据

下载数据之前要先用手机微信扫描网页下方的二维码,加一下数据服务号的微信。对方会让你登记一下你的学校或者单位,然后就可以愉快地下载数据了。

下载好数据之后,我们在 MapInfo 里面导入一下。跟刚才一样,选择导入 Shapefile 格式:

image

导入海盗袭击数据的 CSV 文件

接下来我们来看看我们的海盗袭击数据。下载的文件目录下的 pirate_attacks.csv 文件就是我们需要的海盗袭击数据的文件。

我们先在 Excel 中打开看看里面的内容是什么样子的:

image

可以看见这里 longtitudelatitude 两列就是我们需要的地理位置数据。有部分数据的地理位置信息缺失了,不过大部分数据都很完整,不要紧。

我们在 MapInfo 中导入 CSV 文件。

导入的方法也很简单,首先我们要把 CSV 文件复制到 MapInfo 文件夹下面。(MapInfo 导入 CSV 文件默认生成的图层默认在 CSV 文件的同一级目录下面,所以要先把 CSV 文件复制过来。)

在 MapInfo 中选择 FileOpen,文件类型选择 Comma delimited CSV (*.csv),找到我们刚才复制的 CSV 文件,打开。

image

这里弹出的窗口是在问你按照什么编码格式打开文件(如果有中文的话,这里选错了就会乱码。)。我们的文件是 UTF-8 格式的,但是因为 MapInfo 软件太老了,不支持这种格式。所以我们只好保持原来的设定;

勾选 Use First Line for Column Titles,这个的意思是选择数据表的第一列作为列标签。

点击 OK

image

可以看见,这里 csv 数据表已经导入成功了。但是要知道,在 MapInfo 软件里面,数据表导入默认是不带有地理信息的纯数据表。

我们想要选择通过数据表中的地理信息生成相应的地理坐标点,则需要使用 MapInfo 中“生成点”的操作。

我们选择 Table --> Create Points...

image

然后会弹出生成点选项菜单。我们在 using Symbol 菜单中选择点标签为 size = 18 的红色圆点;同时,软件已经自动为我们匹配了列标签 longitudelatitude 为经纬度坐标。

image

点击 OK,图层没有马上出现。这是因为图层生成之后默认在 CSV 文件同一目录下而没有自动添加到地图中。

我们需要手动吧图层添加进来。我们点击图层管理器一侧的 + 符号,添加地图。

可以看见这个时候海盗袭击数据的点图层就已经添加进来了。

我们手动调整一下途中各个元素的形状样式:将航线调成较粗的黄色线、海盗袭击记录改为红色 5 号尺寸的点。

image

非常的好。

导入世界海区图层数据

接下来我们导入世界海区数据图层(操作跟之前一模一样,我就不赘述了。)

……OMG,图层文件太大 MapInfo 直接卡死了。电脑发出了喷气式客机的声音……

遇到这种情况我们要有耐心,毕竟 MapInfo 12 是接近十年前的古董软件了,导入地图慢也是很正常的。我们要有耐心,多多包容。这个世界地图的导入大概需要半个小时的时间…… (内心 OS:这屁大点事我要是在 QGIS 或者 Python 里三分钟就能搞定)

导入好之后基本就是这个样子的。为了好看我把海区的线条颜色调成了灰色:

image

然后为了保证我们这个程序不会再卡很久,我们创建一个新的空白图层 Malacca.TAB,图层的表结构写成和之前的海区图层一样的形式:

image

然后回到刚才的世界海区图层,在地图上面找到马六甲海区。

马六甲海峡位于马来半岛与印度尼西亚的苏门答腊岛之间,素有东方生命线之称;

  • 位置大概在东经 99 度、北纬 4 度左右。
  • 形状特点:西北面为一条直线状的漏斗形。

image

我们复制这个形状,按 Ctrl + C,直接复制这个形状面再黏贴到新建的图层里面。

接下来我们就可以直接把我们的世界海区图层给移除出去了。这个文件太大,不方便我们操作。

接下来我们简单调整一下各个图层的显示顺序和颜色、线条粗细、样式等等。(图层管理器上方向上和向下的箭头的按钮可以向上 / 向下调整当前图层的位置,这样就可以图层的顺序)

现在地图看上去是这个样子的:

image

设置图层文本标签

接下来我们来设置一下 世界国家Malacca 这两个图层的标签设置。

右键图层打开标签设置菜单,在菜单中如下设置:

image

对标签字体进行以下设置:

  • 字体大小:20(截图中没有体现出来)
  • 字体:任意(找个好看的)
  • 字体背景:发光(Halo)
  • 字体样式:阴影(shadow)

image

每个图层右侧都有 3 个按钮。最右侧的标签形状的按钮就是“显示标签”。我们来点击这个按钮:

image

切割和筛选需要的区域

接下来我们备份一下已经做好的图层。因为我们其他的图层部分就不需要了,所以可以直接删掉。

首先我们新建一个图层 FrameBlock.TAB,图层字段只要 ID (Integer) 就足够了。

image

我们在马六甲地区画一个方框,覆盖我们需要的区域。

image

接下来,针对每一个图层,将图层所有元素选中,然后设为目标;再选择擦除框外元素。

关于这部分的操作我这里没有截图。为什么没有截图呢?因为我的 MapInfo 经不住这些数据的蹂躏,电脑已经发出了米格 31 的声音(悲)

截屏很不方便,所以我就用文字描述一下操作吧。

首先,在左侧图层管理器中找到一个图层。右键选择 Browse Table...

我们可以看见在图层属性表的左上角的有一个指向右下角的等腰直角三角形。这个三角形可以一次性选中图层中所有对象。

我们点击这个三角形,然后再点到图层查看器就能看见当前图层中的所有要素被一次性全部选中了。

接下来的操作和上一次讲的那个分割线对象是一样的:右键我们刚才在 FrameBlock.TAB 图层里面画的方框,选择 Erase Outside...,就可以将图层其余的部分移除了。

image

在这个过程中,可以参考下面的提示:

  • 如果遇到图层无法编辑,可能是因为您没有把图层设置为可以编辑的模式。这个时候可以尝试点击图层旁边小铅笔和纸张形状图标的按钮(开启图层编辑按钮)

  • 如果遇到图层无法编辑,并且点击图层旁边小铅笔和纸张形状图标的按钮(开启图层编辑按钮)没有反应,可能说明这个图层被设置为了只读模式。这是因为我们之前有若干组数据是从外部读入的。

    • 处理步骤如下:
    1. 选中无法编辑的图层
    2. 点击上方标签栏中的 Save Copy As...,另存图层为 .TAB 文件。(注意命名要与原先不同)
    3. 重新读入另存的图层,重复“选中 --> 设为目标 --> 选中方框 --> Erase Outside...”操作。

image

比如你们可以看到在我这里 pirate_attack.TAB 图层就出现了无法编辑的问题。我将图层另存为了 Priate_Attack__.TAB

检查地图中存在的问题和错误

放大一下,检查一下我们的图层有没有什么问题。

咦,这个地方的航线怎么都跑到地上来了啊……

看样子,这可能是因为航线数据集的原作者在制作世界航线地图的时候,遇到了类似于我们上面导入世界国际海图的时候因为数据太大导致的卡顿问题,所以对航线进行了要素边界简化。

由于我们现在要制作局部的小地图,所以如果不把航线画清楚,就会导致地图可读性变差。所以我们要对这里的航线进行调整。

image

世界港口数据集

我们在网上找到一份世界港口数据集的资料。我找到的资料来自这个网站:ArcGIS Hub - International Ports Index

我们下载所有港口的 Shapefile 文件,导入到 MapInfo。可以看见,下面这些就是马六甲地区的港口。

image

修正航线分布

我们还是和之前一样:通过

  • 另存港口图层为 Port__.TAB 以开启编辑
  • 删除图层 Port.TAB,导入允许编辑的图层 Port__.TAB
  • 设置所有港口点为目标
  • 创建选择需要的港口的框,选中
  • Erase Outside... 擦除不需要的马六甲地区之外的港口

然后我们调整航线的形状,跟这些港口接在一起。

注意这里有一根航线实际上是四根航线重叠在一起了。要把这四根航线分开才行。

image

布局和打印

接下来,为了获得一张体面的打印地图,我们需要先设置图例项。

首先我们要先根据自己的喜好调整地图的显示。我这边的设置如下:

  • 海盗袭击记录:三角形,尺寸 9
  • 港口位置:小旗子,尺寸 36
  • 陆地 / 国家行政区划:黑色斜线填充,二密
  • 马六甲海峡:黑色斜线填充,三密
  • 航线:层叠样式,宽深蓝色底线加白色短划线

首先根据地图窗口,创建图例窗口 Map --> Create Legend,弹出菜单之后一直 Next 就能完成设置。

然后根据自己的喜好设置图例项的各项内容。双击图例就能设置了:

image

然后新建布局。选择 Windows --> New Layout Windows...

因为我们这里只打开了一个地图窗口,所以直接选择第一项就可以了。

image

新布局创建好之后默认是下面这个样子的。位置非常混乱,所以我们要手动调整。

image

首先,这个纸张是横过来的,显然不符合我们的需求。但是这个时候你会发现不知道在什么地方调解打印设置。

如果需要调节打印设置,你可以按照如下步骤进行操作:

  1. 什么也不管,直接按下 Ctrl + P,开始打印。
  2. 在弹出菜单中找到 Properties
  3. Properties 中设置纸张横向。
  4. 确定OK,然后就会弹出保存打印文件的菜单。
  5. 直接按下 Esc 取消。软件提示打印失败。

然后你就会发现纸张横过来了!(内心 OS:软件在打印布局的选项卡里面是不是都还没有想到要设置一个把纸张横过来的功能还是怎么的)

image

然后在 Layout 下调整地图要素、比例尺要素、图例要素的大小、位置、排列先后等,调整好之后只要按下 Ctrl + P,确定之后就可以导出为 PDF 文件了。

地图的最终呈现效果如下图所示:

image

最后的整理

最后我们需要清理一下数据:

  • 删除不需要的属性表
  • 压缩所有的属性表
  • 更新所有的 ID 列

大功告成。


更新:2023 年 10 月 17 日

老师讲了专题地图之后,更改了作业要求。要求在作业中使用 MapInfo 工作区和专题地图。我这边原本准备提交了,但是因为有了新的要求,所以作业里面的一些东西也要修改了。

工作区的保存

在绘制专题地图之前,必须要先保存工作区才行。所以我们首先在之前的 ./mapinfo 文件夹下面新建一个文件夹,叫做 layers,把所有的图层文件都保存进去。

接下来,点击其中一个 .TAB 文件。然后再在打开的 MapInfo 窗口的 File 选项卡中点击 Open...,逐个找到之前保存的每一个 .TAB 文件打开。

这里吐槽一个问题,就是我们的这个 MapInfo 软件,它的这个文件管理器非常的难用。首先文件管理器不允许你输入文件的路径直接找到目录,也就是说,你必须不停地用鼠标点点点才能找到路径,不能点开另一个已经打开的文件夹然后直接复制上面的路径黏贴进去来快速查找。所以你每一个文件都要在里面点半天。但是没有办法,因为我们的课程要求用 MapInfo,所以你只能硬着头皮用。

打开了所有工作表之后,就可以点击 File,在下面找到保存工作区的选项 Save Workspace...。把文件保存在 ./layers 文件夹的同一级目录下面。MapInfo 工作区文件的后缀名是 .WOR。我这里命名叫做 main.WOR,此时文件目录树如下:

mapinfo/
│ layers/
│ main.WOR

重置海盗袭击记录图层

重新处理数据

创建专题地图的选项在这里:

image

那么这是不是就是说我们可以愉快的开始创建专题地图了呢?

事实上并不行。在创建专题地图的时候,遇到了下面的情况:

image

发现 next 按钮是灰色的无法点击;同时也没有办法选择想要的属性值。

经过排查之后,发现问题在于原始数据里面的日期 date 这一列的信息有点混乱:大部分的数据的日期信息是写作 XXXX-XX-XX 的格式,但是有一些乱七八糟的数据点是用 XXXX.XX.XX 的格式的,还有一些把其他乱七八糟的信息都带在里面了。而 MapInfo 软件因为太古老了,还没有现代化的功能;既不能自己处理数据,也不能主动丢弃没有用的乱数据。

遇到这种情况,能够熟练使用Excel的同学现在就非常吃香了对不对,可以首先设置日期格式,然后按照这一列排序直接找到日期错误的项,直接使用分隔列的操作把年份从前面的日期里面筛选出来。可惜我的 Excel 技术并不是很好。所以我选择祭出神器:IPython Shell。

在保存数据文件的终端里面打开 Powershell,输入 IPython 启动。(安装过 Jupyter 就可以直接调用 IPython Shell 了。)查看一下当前目录下的文件:

Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.16.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: ls
 驱动器 C 中的卷没有标签。
 卷的序列号是 2EB9-B3AB

 C:\Users\asus\Desktop\交通地理\作业\交通地理作业20230928\data\Maritime-Pirate-Attacks\data\csv 的目录

2023/10/17  10:56    <DIR>          .
2023/10/17  10:56    <DIR>          ..
2023/09/28  08:39             9,239 country_codes.csv
2023/09/28  08:39           601,697 country_indicators.csv
2023/09/28  08:39         1,849,431 pirate_attacks.csv
               3 个文件      2,460,367 字节
               2 个目录 11,290,157,056 可用字节

接下来我们导入 pandas 工具包,用于筛选数据。

In [6]: import pandas as pd

In [7]: df = pd.read_csv("pirate_attacks.csv")

In [8]: df
Out[8]:
            date       time  ...  vessel_status    data_source
0     1993-01-02        NaN  ...            NaN  mappingpiracy
1     1993-01-04        NaN  ...            NaN  mappingpiracy
2     1993-01-06        NaN  ...            NaN  mappingpiracy
3     1993-01-08        NaN  ...            NaN  mappingpiracy
4     1993-01-12        NaN  ...            NaN  mappingpiracy
...          ...        ...  ...            ...            ...
7506  2020-12-15  02:15 UTC  ...            NaN            imb
7507  2020-12-19  13:36 UTC  ...            NaN            imb
7508  2020-12-21  04:09 UTC  ...            NaN            imb
7509  2020-12-26  01:30 UTC  ...       Anchored            imb
7510  2020-12-29  16:40 UTC  ...            NaN            imb

[7511 rows x 16 columns]

导入 datetime 库,用于处理日期信息。

首先将日期中的日期列转换为标准日期,然后从标准日期中筛选出年份增加为一个新的列 'year'。丢弃没有转换成功的数据 dropna

In [9]: import datetime

In [10]: df['date'] = pd.to_datetime(df['date'], errors='coerce')

In [11]: df = df.dropna(subset=['date'])

In [12]: df['year'] = df['date'].dt.year

In [13]: df['year']
Out[13]:
0       1993
1       1993
2       1993
3       1993
4       1993
        ...
7506    2020
7507    2020
7508    2020
7509    2020
7510    2020
Name: year, Length: 7511, dtype: int32

最后把筛选后的数据另存为 ./pirate_attacks_filtered.csv

In [14]: df.columns
Out[14]:
Index(['date', 'time', 'longitude', 'latitude', 'attack_type',
       'location_description', 'nearest_country', 'eez_country',
       'shore_distance', 'shore_longitude', 'shore_latitude',
       'attack_description', 'vessel_name', 'vessel_type', 'vessel_status',
       'data_source', 'year'],
      dtype='object')

In [15]: df[['date', 'year','vessel_name', 'vessel_type', 'time', 'longitude',
    ...: 'latitude']].to_csv("./pirate_attacks_filtered.csv")

In [16]:

现在就有了我们想要的经过筛选的数据:

In [17]: df_f
Out[17]:
        ID        date  year  ...       time   longitude   latitude
0        0  1993-01-02  1993  ...        NaN  116.966700  19.700000
1        1  1993-01-04  1993  ...        NaN  116.000000  22.350000
2        2  1993-01-06  1993  ...        NaN  115.250000  19.670000
3        3  1993-01-08  1993  ...        NaN  124.583300  29.900000
4        4  1993-01-12  1993  ...        NaN  120.266700  18.133333
...    ...         ...   ...  ...        ...         ...        ...
7506  7506  2020-12-15  2020  ...  02:15 UTC    4.751944   2.000833
7507  7507  2020-12-19  2020  ...  13:36 UTC    6.169167   2.602222
7508  7508  2020-12-21  2020  ...  04:09 UTC    5.616667   2.616667
7509  7509  2020-12-26  2020  ...  01:30 UTC   -1.683333   4.883333
7510  7510  2020-12-29  2020  ...  16:40 UTC    6.400000   2.733333

[7511 rows x 8 columns]

好了。现在再把 .csv 图层导入 MapInfo 软件就好了。趁着这个机会我可以再详细的讲一下 MapInfo 软件里面导入 Excel 数据表图层的操作。

每次把 Excel 表格文件导入 MapInfo 图层都需要下面的这么几步操作:

  1. 点击 File --> Open...,在一大堆文件里面找到需要导入的 CSV 文件
  2. 勾选“使用第一行作为列名”
  3. 直接导入进来的数据仅仅只是纯表格。所以我们要生成点。点击 Table --> Create Points...
  4. 设置采用哪两列作为经纬度坐标
  5. 创建完点图层之后,点图层是只读格式的。所以我们必须把图层另存一遍。
  6. 另存之后关闭另存前的图层
  7. 点击 File --> Open...,把另存的不是 Read Only 的图层打开到图层管理器

但是这样会遇到一个问题,就是之前的那个只读的图层会被保存在和 Excel 表格同级的目录下面。如果你想删除这个没有用的只读图层,那你需要:

  1. 保存当前所有文件,然后关闭 MapInfo 软件(如果不关闭,那么之前的只读图层就会一直是占用状态无法删除)(你最好保存工作区)
  2. 找到 Excel 工作表所在目录,删掉四个只读的图层文件。
  3. 重新运行 MapInfo

别着急。你以为这就完了吗?不不不,没有这么简单。MapInfo 软件在你删除了这张只读的图层的工作表之后,工作区文件就会因为找不到这张表格而出现报错。

这个时候,你就要手动选择:

  1. 选择仅使用现在能够找到的工作表文件打开工作区
  2. 继续在弹出的窗口里重新另存工作区文件,然后覆盖掉原来的工作区

另存的工作区里是没有这张数据表的,不会出现报错。

总的来讲,MapInfo 读取一次 .csv 文件的数据,就要重启两次软件。我也不知道这个奇葩设计是谁想出来的,反正我有一种想把设计师打一顿的冲动。

好吧。我这里就不演示上面的步骤了,因为上面的步骤又繁琐又混乱。

下面的图展示了重新清洗之后的数据表的导入效果。

image

然后下面这张图是根据上面的数据表重新创建的海盗袭击数据点的矢量图层。

image

接下来我们就可以创建专题地图了。

创建专题地图

按理来说到这个时候我们就可以愉快的开始创建主题地图了。不过在此之前先等等我就是有个疑问,就是我的专题地图功能为啥只有三个选项?

image

算了不管了。反正我们是要对海盗袭击数据按年份创建主题地图嘛,那我们就选择 Point Ranges Default

image

然后在这个界面我们可以手动设置图例选项。

MapInfo Miscellaneous 这个图标集里面提供了这个海盗的骷髅头的图标,放在这里刚刚好。

image

主题地图的生成效果就是下面这样的。感觉颜色还是太鲜艳了一点。

image

最后重新调整所有的元素、标签、图例,重新打印,就大功告成了。

成品展示

下图展示了新加坡周边海域的海盗袭击事件情况。

image

下图展示了整个马六甲海区的海盗袭击事件情况。

image

posted @ 2023-09-29 19:54  多玩我的世界盒子  阅读(53)  评论(0编辑  收藏  举报