kikita & Maps

GIS,spatial and artificial intellegence learning and share

这是一篇日记。

最近,终于把年初我和马老师的脑洞项目 局部更新矢量切片 放在了大型程序员G友交流社区,开始出 release了……

在项目的开始,这个需求来自于用户的真实需求,如下是正经的背景信息:

矢量切片是Esri在新版本中推出的一项亮点新技术,通过ArcGIS Pro中的Create Vector Tile Service工具生成切片包,再通过Portal发布成 Vector Tile Service,在前端支持各种应用,采用统一的接口进行调用,实现了一体化的平台应用解决方案。矢量切片技术充分利用GPU的渲染能力,以全新方式在设备、浏览器中以矢量的方式展现缓存地图。这项技术解决了传统基于栅格缓存切片展现地图存在的诸多问题:如设备分辨率对地图渲染效果的影响,缓存创建后无法再动态更改样式,生成缓存切片的周期过长,对硬件的需求过大等等。

众所周知,矢量切片包的生成速度相对于传统的栅格切片包具有指数极的性能提升,这也是体现矢量切片优越性的一个重要方面。对于一个常规数据量在100MB-10GB级的数据,全部生成一次矢量切片包的速度在分钟级。可能正是因为这个原因,截至目前最新版本(ArcGIS Pro 2.2),Esri仍未提供局部更新矢量切片包的功能。虽然矢量切片生成速度很快,但是到了TB级别的大数据来说,生成一次完整的切片包也是需要相当长的时间的,如果仅是更新了局部的小范围数据,那基于更新范围自动化更新矢量切片就是一个非常有实际应用价值的需求。 正是在这样的背景下,我们发起了创建自动化局部更新矢量切片工具的项目。

—— @马老师

既然是两个人的项目,又会涉及很多版本的更新同步问题,Why not Github

我们一共设计了5个Python工具,用于扩展ArcGIS Pro的功能,分别实现原始矢量切片包的创建,局部矢量切片包的创建,以及矢量切片包的更新,矢量切片服务的更新,托管在阿里云平台的矢量切片服务的更新。

截止到今天的版本 Partially Upate ArcGIS Vector Tiles Toolbox 1.1.0

Mark:

后续还有很多有待优化的关键点:

  • Bundle空间范围的精确计算
  • LOD值的自动计算

CityEngine 的CGA建模过程是个不断迭代繁衍的过程,我们将这个过程想象成一棵树。

通过 Shapefile,File Geodatabase,AutoCAD DXF 这些矢量数据格式导入CityEngine中的2D基面(Footprint),通常称为 CityEngine Initial Shape,这是这棵树的根节点。通过CGA中的命令符 --> ,将一个shape经过各种形状(Geometry)和位置(Pivot,Scope) 的函数操作,生成另一个shape。直到最后,模型建立,最终的shape节点,通常称为 CityEngine Leaf Shape

这个细节的概念,在2013年的开发者大会中提过一次,结合模型层次结构的幻灯片理解下:

完整的幻灯片可以点 这里 下载。

为什么说是“迭代繁衍”? 因为除了 Leaf Shape 之外的所有shape 在建模过程中都被紧跟它其后的shape替代而消亡了。

举例子,在这条规则中,

1
2
A --> function() B 
B --> function() C

这条规则中 ,A shape 对象已经消亡被B替代,在后续的规则中无法再调用到 A, 如果希望取到与A相同的对象,就需要提前复制出来,如下:

1
2
3
A --> Anew function() B            //A被复制而创建了分支
B --> function() C
Anew --> function() D

了解了这些,那么我们可以进一步回答这个问题:“如何将CityEngine规则建模的模型导出成各组件独立存储的模型?”

可以的,再导出FileGDB或 Scene Layer Package的时候选中 “One Feature per Leaf Shape” 即可。

Export to FileGDB

Export to Scene Layer Package

这些导出的模型数据拿到其他软件中使用,例如ArcGIS Pro,各组件即为不同的静态模型对象。

这个示例的CGA脚本在 我的Github 可以查看和下载。

Esri CityEngine 是一款面向于大规模城市规划行业的,交互式、沉浸式高级三维建模软件,相比传统建模更加高效省时。 通过CityEngine我们可以基于现实世界的GIS数据,构造城市的现实状态,模拟城市的历史风貌,设计城市的未来蓝图。

如何申请Esri CityEngine 30天试用?

Step 1

访问Esri官方试用地址:https://www.esri.com/en-us/arcgis/products/esri-cityengine/trial

点击“Start CityEngine Trial

Step 2

创建一个Esri账号,点击“Create a Public Account”; 如果已经拥有过 Esri 账号,可以直接登录。

PS:每个邮箱只能申请一次试用。请申请试用时,确保邮箱是第一次注册。

​ 国内不建议使用Facebook和Google账户登录,避免后续登录的麻烦,你懂的。

在新的创建账户的页面中添加,名字、姓氏、邮箱,点击创建账户按钮,直至收到如下提示。

Thank you for signing up.

An e-mail has been sent to [上一步填写过的邮箱]

Step 3

查看自己的邮箱,你会发现类似的一封,点击 “Activate Your Account”。

在弹出页面中,填写标星号的必要信息,例如:用户名、密码、国家、安全问题。然后点击创建账户。

Step 4

选择几项附加信息,点击下一步,会看到最终的包含了类似 EVA123456789 格式授权码的页面。

请记录这个号码以备后用。如果忘记了也没有关系,注册邮箱中也会收到一封 “Esri CityEngine Trial Confirmation”主题的邮件,可以查询。

Step 5

下载并安装 Esri CityEngine 2018 软件。

如果不确认自己的系统是否满足安装要求,请查看 Esri CityEngine 系统要求

大多数情况下,我们安装在 Windows 操作系统中;当然 CityEngine 也支持 Mac OS X,Linux。

安装过程简单,与常规应用程序无差,按向导下一步即可完成,不赘述。

Step 6

启动开始菜单 Esri CityEngine 目录下的 CityEngine 2018 Administrator

选择左侧 CityEngine 节点,右边选择 高级版单机许可,点击下方的 “Authorize Now”。

按照默认设置,一直下一步,并填写带信号的信息即可。

填写入CityEngine的授权号码,下一步,直至看到祝贺页面即授权成功。

从开始菜单,启动CityEngine软件即可。

​ CityEngine的同义词几乎就是“规则建模”,规则指的是CGA脚本。说到脚本,很多用户又会联想到 Python, 衍生出来的问题就是,“CityEngine中是用python脚本建模吗?” 答案是No。

​ CityEngine中确实也引入了python,只不过使用python的姿势不是建模,而是自动化。通过python脚本,可以增强CityEngine的建模功能,自动化驱动脚本建模工作流,提高建模效率。我们既可以在控制台交互式执行python命令,也可以在python editor 中书写python执行文件,甚至可以将一些自定义功能扩展到CityEngine中。如下是CityEngine官方教程中的几个示例代码:

Example 1 选择部分对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'''
Created on 2018-4-9
@author: kikita
'''
from scripting import *

# get a CityEngine instance
ce = CE()

def selectByAttribute(attr, value):
objects = ce.getObjectsFrom(ce.scene)
selection = []
for o in objects:
attrvalue = ce.getAttribute(o, attr)
if attrvalue == value:
selection.append(o)
ce.setSelection(selection)

if __name__ == '__main__':
selectByAttribute("connectionStart","JUNCTION")

Example 2 修改对象属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'''
Created on 2018-4-9
@author: kikita
'''
from scripting import *

# get a CityEngine instance
ce = CE()

''' increment the street width parameter of all selected street segments'''
# @noUIupdate
def incrementStreetWidths(increment):
selectedSegments = ce.getObjectsFrom(ce.selection, ce.isGraphSegment)
for segment in selectedSegments:
oldWidth = ce.getAttribute(segment, "/ce/street/streetWidth")
newWidth = oldWidth+increment
ce.setAttribute(segment, "/ce/street/streetWidth", newWidth)

if __name__ == '__main__':
incrementStreetWidths(10)

Example 3 动态模拟建筑物生长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
'''
Created on 2018-4-12
@author: kikita
'''
from scripting import *

# get a CityEngine instance
ce = CE()

def growBuilding():
for i in range(1,14):
height = 20+i
doStep(i,height,1)
for i in range(15,35):
height = 34
width = i-14
doStep(i,height,width)

def doStep(i,height,width):
object = ce.getObjectsFrom(ce.scene, ce.withName("'Lot1'"))
ce.setAttributeSource(object, "height", "OBJECT")
ce.setAttributeSource(object, "width", "OBJECT")
ce.setAttribute(object, "height", height)
ce.setAttribute(object, "width", width)
Generate(object)

def Generate(object):
ce.generateModels(object)

if __name__ == '__main__':
growBuilding()

Example 4 陈列资源库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
'''
Created on 2018-4-12
@author: kikita
'''
from scripting import *

# get a CityEngine instance
ce = CE()

def writeCGAlib():
# open the cga file to be written
cga = "/*Asset Library Loader : Generated by asset_lib.py*/\n version \"2014.0\"\n\n"
# write start rule
cga += "Lot --> Geometries Textures"
# write rule showing geometries
cga += "\n\nGeometries --> "
# get all .obj files from asset directory, and call their loader
for obj in ce.getObjectsFrom("/", ce.isFile, ce.withName("/Tutorial_10*/assets/*.obj")):
# and write
cga += "\n\t t(2,0,0) Geometry(\""+obj+"\")"
print obj
# write rule showing jpg textures
cga += "\n\nTextures --> \n\ts(1,0,0) set(scope.tz,0) set(scope.ty,3) i(\"facades/xy-plane.obj\")"
# get all .jpg files from asset directory, and call their loader
for jpg in ce.getObjectsFrom("/", ce.isFile, ce.withName("/Tutorial_10*/assets/*.jpg")):
cga += "\n\tt(2,0,0) Texture(\""+jpg+"\")"
#write geometry loader rule
cga += "\n\n Geometry(asset) --> s(1,0,0) i(asset) set(scope.ty,0) set(scope.tz,0)"
#write texture loader rule
cga += "\n\n Texture(asset) --> set(material.colormap, asset)"
cgafile = ce.toFSPath("rules/asset_lib2.cga")
CGA = open(cgafile, "w")
CGA.write(cga)
CGA.close()
print "written file "+cgafile

def assignAndGenerateLib():
object = ce.getObjectsFrom(ce.scene, ce.withName("'Lot2'"))
ce.refreshWorkspace()
ce.setRuleFile(object, "asset_lib2.cga")
ce.setStartRule(object, "Lot")
ce.generateModels(object)

if __name__ == '__main__':
writeCGAlib()
assignAndGenerateLib()

Example 5 扩展CityEngine功能

相关的python功能实现后,确保在CityEngine的python editor中可以正常运行。然后,在CityEngine的工作空间根目录创建 scripting.py 文件,内容如下:

1
2
3
4
5
6
7
import sys

sys.path.append({PATH_TO_YOUR_SCRIPTS_DIRECTORY})
# e.g. sys.path.append("C:\user\CityEngine\MyProject\scripts")

# import customized python module
import myEfficiencyModule

重启CE之后,可以在python编辑器或控制台中直接使用此模块的功能。

1
>>> myEfficiencyModule.selectByAttribute("connectionEnd", "JUNCTION")

摄于 2014-5-9, Acropolis, Athens, Greece

CityEngine的教程中刚好有个Parthenon Temple 的 Demo

不禁会想起在雅典卫城里它的断壁残垣,晒晒我们的合影

不知道几年过去,修复工作又向前推进了多少

那些带有编号的石头是不是已经归位了呢

整个卫城中我最喜欢的是上图中最后一张的“厄瑞克忒翁神庙”,据说用于供奉雅典娜、波塞冬和英雄厄瑞克忒翁。

神庙一侧的女神柱像精致优美,即使经过了这么多的时光,都可以感受到雕刻师的精湛技艺。

厄瑞克忒翁神庙

0%