Mapbox架构

概述

本文是针对native sdk 4.0进行学习总结,以ios平台为例进行的架构总结,方便后续工作的开展。由于时间和认知原因,总结可能存在不当之处,随着后续的理解加深,将进行不断的完善和修正。

系统分层

Mapbox架构_第1张图片

系统分层图

如上图所示为,地图SDK的系统分层,总体上分为两层,地图内核层(图中虚线框部分)和SDK封装层(图中上部紫色框)。

  • 地图内核层:是地图显示与控制功能的实现层,主要是C++实现,部分平台相关的由各平台语言实现,内核层又可以分为数据IO层、资源管理层、渲染组织层、渲染接口层、地图控制层等。
    • 数据IO层:负责资源数据的加载、解析、数据缓存等,包括缓存数据和网络数据等;
    • 资源管理层:负责地图资源的管理调度,资源包括地图样式、图片、文字、地图数据等;
    • 渲染组织层:负责将地图数据和样式,生成渲染所需数据,渲染状态控制、渲染排序等;
    • 渲染接口层:负责对渲染API(OpenGL、gles)的封装,实现渲染的跨平台性;
    • 地图控制层:负责对地图的控制,包括位置、层级、风格等,负责事件的分发处理,负责对外(sdk层)接口的封装;
  • SDK封装层:负责地图引擎空间部分的实现,负责根据不同平台实现不同的语言的封装,如iOS的OC封装、Android的JNI封装等。

模块说明

在本节中,将对mapbox的模块进行说明,每个模块为一个工程或者为多个类和文件的集合。

Mapbox架构_第2张图片

模块图关系图

如上图所示,mapbox的模块逻辑图,图中箭头代表了模块间的依赖关系(由于在实现中大量使用的Bridge模式,所以依赖关系存在一些出入)。虚线框代表上一节所述对应颜色的分层。

每个模块的功能说明如下:

  1. 基础库:该部分封装一些与业务无关的公共库
  • Vendor:为一些第三方的开源库,如boost,geojson,sqlite等;
  • util:定义实现了一些基础的功能类。如Thread,timer,geometry,image,event,task等,这些基础功能与业务无关,为上层的业务模块所调用;
  • actor:任务的调度管理基础库,实现了不同的活动者(线程,或者线程池)之间的任务通知,分配以及处理机制;

2. 渲染接口

  • Gfx: 对渲染环境的抽象,对渲染概念的抽象和枚举定义,该模块不依赖opengl。如深度、模板测试等等的抽象定义;
  • gl:是对opengl相关对象的封装,依赖于opengl和gfx。如Vbo,Fbo,uniform等概念进行封装,真正与opengl进行交互;
  • Program:是对各种渲染shader的管理模块,包括shader的管理,calldraw调用等。(源代码中将shader独立为一个单独的模块,现改造为合并到program模块中)。

3. IO

  • Storage:负责数据的加载、下载、缓存等功能
  • Sprite:主要负责中图片资源的下载,解析等操作
  • conversion:在style模块下,负责样式文件json的解析等,包括图层、数据源、过滤器、表达式等;
  • Geometry:矢量瓦片数据解析的辅助模块、DEM数据的操作,该模块没有统一的功能集合,难以归纳;

4. 资源

  • Style:样式的管理模块,与样式文件相对应,包括图层(style::Layer)的管理,数据源(style::Source)的管理,各种样式组成的定义,如属性、表达式、灯光、图片等;
  • Tile:瓦片数据的定义管理模块,定义了各种瓦片以及相应操作,包括栅格瓦片、矢量瓦片、地形瓦片、json瓦片等;
  • Text:文字的管理模块,包括文字数据、文字纹理生成、碰撞检测等相关功能;
  • Algorithm:主要是瓦片ID相关的计算操作;

5. 渲染组织

  • Render:地图显示相关的在引擎的组织管理模块,包括渲染数据组织(renderlayer),渲染队列组织、渲染数据生成(bucket)、显示区域计算、渲染控制等功能;
  • Layermanger:工厂模块,负责创建对象style::Layer、RenderLayer、Bucket、Layout等;
  • Layout:负责渲染对象的布局相关的操作,符号(symbol)的摆放等;

6. 内核接口

  • map:地图的整体控制模块,负责相机的控制计算、地图的变换、事件定义、对外接口等功能;
  • Annotation:负责地图叠加层的管理,叠加层包括点、线、面等,叠加层的瓦片化组织管理等;

7. SDK封装

不同平台上的接口封装,内部还会有好多子模块,包括窗口、控件、样式、地图控制、叠加层等功能;

线程模型

本章主要负责描述,在引擎中主要的线程,以及每个线程主要的功能。一些系统创建的临时线程不在该章描述。

  1. 主线程(渲染线程)

创建者:系统创建

生命周期:在应用启动时创建,在退出时结束

说明:在iOS中渲染线程为主线程,用于界面的绘制,手势的处理,地图的绘制以及向其他线程发送数据请求等。

2. worker线程池

创建者:主线程创建

生命周期:在应用启动时创建,在退出时结束

说明:共创建了4个线程,用于执行瓦片数据的解析和构建(TileWorker),图片数据的解析(SpriteLoaderWorker)等操作;

3. AssetFileSource

创建者:主线程创建

生命周期:在应用启动时创建,在退出时结束

说明: 负责Asset数据的加载,当前程序中该线程运行较少,后期再做详细补充

//Asset request const std::string assetProtocol = "asset://";

4. DefaultFileSource

创建者:主线程创建

生命周期:在应用启动时创建,在退出时结束

说明:负责数据的加载功能,包括本地数据的加载和网络数据下载请求(不负责下载,只负责发送请求);

5. LocalFileSource

创建者:DefaultFileSource创建

生命周期:在应用启动时创建,在退出时结束

说明:负责Local file数据的加载,当前程序中该线程运行较少,后期再做详细补充

//Local file request const std::string fileProtocol = "file://";

6. https下载线程

创建者:DefaultFileSource创建

生命周期:单次下载请求,请求结果返回后结束

说明:主要负责与服务器连接并下载相应的网络数据。

类图及功能说明

本章主要对引擎中,主要的类图进行解释,同时对一些主要的类进行简要的说明,方便对整个引擎的理解。

主要类图

  1. 线程相关类图

Mapbox架构_第3张图片

 

说明:

  • An `Actor` is an owning reference to an asynchronous object of type `O`: an "actor"

Actor类是对Object类对象的异步调用的封装,其中成员变量AspiringActor用于存储Object对象和mailBox指针,EstablishedActor 用于控制对象的生命周期(对象的创建和销毁)和和mailBox的打开和关闭;

  • An `ActorRef` is a *non*-owning, weak reference to an actor of type `O`.

ActorRef是对Object类对象的弱引用,其其提供的异步调用的接口invoke();

  • Message: A movable type-erasing function wrapper.

是对Object对象的方法的抽象化,将函数调用封装为一个Message对象,方便使用Mailbox对其管理MessageImg和AskMessageImg是该类的两种子类,是Message的具体实现;

  • Mailbox:用于存储Message和执行Message,一个scheduler对象会被Attach到Mailbox对象上,用于执行Message;push()方法用于向mailbox中添加一个message(在actorRef::invoke中被调用),receive(maybeReceive)方法用于执行一个message;
  • Scheduler: A `Scheduler` is responsible for coordinating the processing of messages by one or more actors via their mailboxes. It's an abstract interface.

Scheduler是对Mailbox的调度类,该类是一个抽象类,只提供的虚拟接口scheduler(),具体实现由其子类完成,当前的子类有ThreadPool和util::RunLoop;

  • util::RunLoop:继承自Scheduler,在iOS中,采用CFRunLoop实现,附着在一个线程上,详细情况在下一类图说明;
  • ThreadPool:ThreadPool` can coordinate an unlimited number of actors over any number of threads via a pool, preserving the following behaviors:

- Messages from each individual mailbox are processed in order

- Only a single message from a mailbox is processed at a time; there is no concurrency within a mailbox

Subject to these constraints, processing can happen on whatever thread in the pool is available.

ThreadPool有多个线程组成,当前引擎中启动了4个线程,通过std::condition_variable来进行唤醒线程,多个线程抢占Message处理;

2. 请求以及Runloop相关类图

Mapbox架构_第4张图片

 

说明:

  • AsyncRequest:异步请求的基类,一般情况下通过Filesource(其子类)的request方法获得,其中DefaultFileSource、AssetFileSource、LocalFileSource生成的是FileSourceRequest,HTTPFileSource生成HTTPRequest,OnlineFileSource生成OnlineFileRequest;
  • FileSourceRequest:数据请求,由DefaultFileSource生成的request作为数据请求的入口,由主线程创建,由DefaultFileSource线程进行相应,其根据请求类型的不同,分别转化为AssetFileSource、LocalFileSource、OnlineFileSource请求(参见DefaultFileSource::Impl:request方法)
  • OnlineFileRequest:会根据是否在线组处理,若不在线责返回错误,如在线责生成HTTPRequest,转化为http请求;
  • 由于HTTPRequest和OnlineFileRequest是异步返回的,所以请求相关的状态和数据,存放在Response结构体中;
  • WorkRequest:用于可以取消的请求,当前系统中没有使用;
  • WorkTask:RunLoop的执行单元,RunLoop每次执行一个WorkTask,当RunLoop最为Scheduler进行任务调度时,内部会将mailbox转化为一个Task( Mailbox::maybeReceive(mailbox););
  • RunLoop:基于iOS的封装,依赖于一个线程,内部存放了WorkTask的两个队列,对应于两个优先级;
  • AsyncTask:是对CFRunLoop的一些操作的封装,创建时所在的线程就为任务(std::function task)执行的线程,通过maysend方法,来唤醒线程执行想用的任务;其作为RunLoop的一个成员变量是,用于当来任务时,唤醒线程,并通过task执行任务;其业务可以独立于RunLoop单独使用,用于任务的异步处理;

3. FileSource相关类图

Mapbox架构_第5张图片

 

说明:

  • FileSource:基类,表示一种数据源或者说数据加载方式,提供了两个虚接口request和supportsCacheOnlyRequests,用于创建AsyncRequest,以及是否支持cacheOnly请求;
  • 所有的XXXFileSource的数据请求操作都是在对应的Impl类的request函数中实现;
  • DefaultFileSource:数据加载的入口,所有的数据请求都是通过该类对象进行请求,创建对象时,同时会创建一个线程,用于缓存数据的加载和请求的转发;该类对象中,会包含assetFileSource、LocalFileSource、OnlineFileSource的对象,用于请求转发;
  • LocalFileSource、AssetFileSource创建时,也会启动一个对应的线程,当前运行较少,不做详细阐述;
  • OnlineFileSource:在线数据加载,没有对应线程,会把请求转发为HttpFileSource,对象中包含一个为HttpFileSource对象;
  • HttpFileSource:用于https请求网络数据,数据下载会在单独的线程(系统创建)中执行;

4. RenderSource相关类图

 

Mapbox架构_第6张图片

 

 

Mapbox架构_第7张图片

 

说明:

  • 上面类图分为两部分,其中上面Render相关的为渲染组织层的类,方便地图渲染而对数据的组织层;style名称空间内的类为与地图样式相关的类,它们是样式文件(json)在内存的存储结构;
  • 在程序运行时,每一个renderSource与style:source是一一对应的,它们指向同一个style::Source::Impl对象;
  • style::Source : The runtime representation of a [source] (https://www.mapbox.com/mapbox-gl-style-spec/#sources) from the Mapbox Style Specification.

style::Source是source样式在内存中的存储结构,它描述了数据的获取方式和数据类型(并不是真正的数据),根据类型不同分为:vectorSource用于说明矢量瓦片的获取方式;GeoJsonSource表示GeoJson的数据源描述;Raster栅格数据;RasterDEM栅格地形;Iamge 图片数据;custom用户数据;Annotation用户叠加数据;

  • RenderSource:用于渲染时的数据组织与计算,在每帧渲染时Render对象会检测当前存储的renderSource与style中是否有差别,若有就会创建或者删除RenderSource,保证与样式中一一对应。RenderSource用于管理和计算当前条件那些瓦片可见,在一个RenderSource内所有图层有统一的瓦片划分方案(当前多为TilePyramid);
  • TilePyramid:瓦片管理类,负责可见瓦片的计算、管理和缓存功能,在每帧内会计算出需要显示的瓦片,同时会缓存一些暂时不显示的瓦片;
  • Tileset:样式中对应于source文件中的内容,用于瓦片的下载地址等的存储;

5. Layer相关类图

 

Mapbox架构_第8张图片

Mapbox架构_第9张图片

 

说明:

其中style空间内为样式图层在内存中的结构;renderLayer为渲染是的图层组织,渲染时按照图层进行排序,每个图层内的不同瓦片连续渲染;

图层绘制的顺序是样式文件定义的顺序;

6. Tile相关类图

 

Mapbox架构_第10张图片

 

说明:

Tile为数据的组织结构,每个Tile内有多个图层的数据;

7. Map、Render相关类图

Mapbox架构_第11张图片

 

说明:

The RenderFrontend is the bridge between the Map and platform used to update and observer the Renderer. It hides any threading specifics and always replies on the original thread.

重要类说明

主要时序图

  1. 渲染帧

 

Mapbox架构_第12张图片

 

 

Mapbox架构_第13张图片

 

说明:该时序均在渲染线程中执行,部分方法忽略了活动范围;

2. 数据加载(下载)

Mapbox架构_第14张图片

 

说明:

不同的颜色代表不用的线程:红色为主线程,蓝色为线程池,灰绿色为DefaultFileSource线程;绿色为http下载线程;

缓存数据库结构

缓存数据采用sqllite数据库进行存储,表结构相对简单,表内数据采用压缩格式(zlib)进行存储。

  1. 总体数据库结构

Mapbox架构_第15张图片

 

  1. region_resources表

 

Mapbox架构_第16张图片

 

CREATE TABLE region_resources (

region_id INTEGER NOT NULL REFERENCES regions(id) ON DELETE CASCADE,

resource_id INTEGER NOT NULL REFERENCES resources(id),

UNIQUE (region_id, resource_id)

)

2. tiles表

Mapbox架构_第17张图片

 

CREATE TABLE tiles (

id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,

url_template TEXT NOT NULL,

pixel_ratio INTEGER NOT NULL,

z INTEGER NOT NULL,

x INTEGER NOT NULL,

y INTEGER NOT NULL,

expires INTEGER,

modified INTEGER,

etag TEXT,

data BLOB,

compressed INTEGER NOT NULL DEFAULT 0,

accessed INTEGER NOT NULL,

must_revalidate INTEGER NOT NULL DEFAULT 0,

UNIQUE (url_template, pixel_ratio, z, x, y)

)

3. sqlite_sequence表

Mapbox架构_第18张图片

 

CREATE TABLE sqlite_sequence(name,seq)

4. resources表

Mapbox架构_第19张图片

 

CREATE TABLE resources (

id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,

url TEXT NOT NULL,

kind INTEGER NOT NULL,

expires INTEGER,

modified INTEGER,

etag TEXT,

data BLOB,

compressed INTEGER NOT NULL DEFAULT 0,

accessed INTEGER NOT NULL,

must_revalidate INTEGER NOT NULL DEFAULT 0,

UNIQUE (url)

)

5. region_tiles表

Mapbox架构_第20张图片

 

CREATE TABLE region_tiles (

region_id INTEGER NOT NULL REFERENCES regions(id) ON DELETE CASCADE,

tile_id INTEGER NOT NULL REFERENCES tiles(id),

UNIQUE (region_id, tile_id)

)

6. regions表

Mapbox架构_第21张图片

 

CREATE TABLE regions (

id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,

definition TEXT NOT NULL,

description BLOB

)

接口说明

 

附录

你可能感兴趣的:(mapbox,react,native,c++)