【Python】元胞自动机模拟城市扩张

代码实现整体框架

定义一个名叫LandWorld的类,含有类变量:
     Cells:元胞空间(第一幅图)
     Width:宽
     Height:高
     geotransform:存储坐标转换信息
     projection:存储坐标信息
     image2:第二幅图定义邻域情景
     distance:距离栅格数据
     roadscore:各元胞道路评分
     neighborscore:各元胞邻域评分

运行时输入参数:

  1. 第一幅土地利用影像存储路径
  2. 第二幅土地利用影像存储路径
  3. 道路距离栅格影像存储路径
输入参数.png

调用方法Start(),自定义迭代次数N,迭代结果影像(tiff格式)将会以result开头加迭代结果自动命名。


调用方式.png

后期再利用可以在类里面的Updata()修改转换规则

运行效果

当然,运行结果完全不具参考性,仅供玩耍。


原影像.png

100次迭代后.png

完整代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
import gdal
import os

URBAN = 1
THRESHOLDTOURBAN = 0.2  # 转化阈值,高于变城市用地
THRESHOLDTOUNURBAN = 0.4  # 转化阈值,高于变非城市用地
INTERVAL = 100  # 划分“至道路的距离”的区间长度
SELFTOURBAN = [0., 1.0, 0.5, 0.3, 0.8, 0.]  # 本身开发适宜性


class LandWorld:
    width = 100
    height = 100

    def __init__(self, firstImage, secondImage, distanceToRoad):
        try:
            dataset1 = gdal.Open(firstImage)
            dataset2 = gdal.Open(secondImage)
            datasetRoad = gdal.Open(distanceToRoad)
            self.geotransform = dataset1.GetGeoTransform()  # 存储坐标转换信息
            self.projection = dataset1.GetProjection()  # 存储坐标信息
            # 第一幅图像定义元胞空间
            self.cells = dataset1.ReadAsArray()
            self.width = dataset1.RasterXSize
            self.height = dataset1.RasterYSize

            # 第二幅图定义邻域情景,计算邻域影响下的转化概率
            self.image2 = dataset2.ReadAsArray()

            # 至道路的距离是决定是否向城市用地转化的条件之一
            self.distance = datasetRoad.ReadAsArray()

        except:
            print("数据读取出错!")

    def GetWidth(self):
        return self.width

    def GetHeight(self):
        return self.height

    def TryGetCell(self, h, w):
        return self.cells[min(h, self.height - 1)][min(w, self.width - 1)]  # 注意边缘处理

    def GetNearbyUrbanCount(self, h, w):
        """
        统计邻域城市用地数量
        """
        nearby = [self.TryGetCell(h + dy, w + dx) for dx in [-1, 0, 1]
                  for dy in [-1, 0, 1] if not (dx == 0 and dy == 0)]
        return len(list(filter(lambda x: x == URBAN, nearby)))

    def FindDistanceLaw(self):
        """
        获取城市用地与distance to road的关系
        返回每个距离区间的权重
        问题:如何区间间隔定义为interval
        """
        # global interval
        bin = np.zeros(int(np.max(self.distance) // INTERVAL + 1), dtype=np.int)
        for i in range(self.height):
            for j in range(self.width):
                if self.distance[i, j] >= 0:
                    bin[int(self.distance[i, j] // INTERVAL)] += 1
        max = np.max(bin) * 1.0
        WBin = bin / max
        return WBin

    def GetDistanceWeights(self):
        """
        获取道路因素影响下的城市化权重
        """
        WDistance = np.zeros([self.height, self.width])
        WBin = self.FindDistanceLaw()

        for i in range(self.height):
            for j in range(self.width):
                WDistance[i, j] = WBin[int(self.distance[i, j] // 100)]
        return WDistance

    def FindNeighborLawToUnurban(self):
        """
         两期土地利用数据统计城市用地到非城市用地转变的邻域规律
         返回各邻域情景下(城市->非城市)的比例
         """
        scenes = np.zeros(9, dtype=np.int)  # 下标代表邻域城市用地数量
        tounurban = np.zeros(9, dtype=np.int)
        for i in range(self.height):
            for j in range(self.width):
                if self.cells[i, j] == URBAN:
                    n = int(self.GetNearbyUrbanCount(i, j))
                    scenes[n] += 1
                    if not (self.image2[i, j] == URBAN):
                        tounurban[n] += 1
        return tounurban / (scenes + 0.0001)

    def FindNeighborLawToUrban(self):
        """
         两期土地利用数据统计非城市用地到城市用地转变的邻域规律
         返回各邻域情景下(非城市->城市)的比例
         """
        scenes = np.zeros(9, dtype=np.int)  # 下标代表邻域城市用地数量
        tourban = np.zeros(9, dtype=np.int)
        for i in range(self.height):
            for j in range(self.width):
                if not (self.cells[i, j] == URBAN) and not (self.cells[i, j] == 0):
                    n = int(self.GetNearbyUrbanCount(i, j))
                    scenes[n] += 1
                    if self.image2[i, j] == URBAN:
                        tourban[n] += 1
        return tourban / (scenes + 0.0001)  # 防止分母为零

    def GetNeighborWeight(self):
        """
        获取邻域影响权重
        返回每一个元胞单元的邻域权重
        城市单元(->非城市权重),非城市单元(->城市权重)
        """
        WNeighbor = np.zeros([self.height, self.width])
        urban = self.FindNeighborLawToUrban()  # 获取邻域情景对应的转化概率
        unurban = self.FindNeighborLawToUnurban()

        for i in range(self.height):
            for j in range(self.width):

                # 如果是城市元胞,统计变成非城市用地的概率
                if self.cells[i, j] == URBAN:
                    n = self.GetNearbyUrbanCount(i, j)
                    WNeighbor[i, j] = unurban[int(n)]

                # 如果是非城市元胞,统计变成城市用地的概率
                elif not (self.cells[i, j] == 0):
                    n = self.GetNearbyUrbanCount(i, j)
                    WNeighbor[i, j] = urban[int(n)]
        return WNeighbor

    def Update(self):
        """
        更新元胞
        """
        for i in range(self.height):
            for j in range(self.width):
                if self.cells[i, j] == URBAN:
                    # 城市用地:是否变成非城市用地(统一变成草地4)
                    score1 = (1 - self.roadscore[i, j]) * self.neighborscore[i, j]
                    if score1 > THRESHOLDTOUNURBAN:
                        self.cells[i, j] = 4

                elif not (self.cells[i, j] == 0):
                    # 城市用地:是否变成非城市用地
                    score2 = self.roadscore[i, j] * self.neighborscore[i, j]
                    if score2 > THRESHOLDTOURBAN:
                        self.cells[i, j] = 1

    def Start(self, N):
        """
        CA开始,自动迭代
        """
        self.roadscore = self.GetDistanceWeights()
        self.neighborscore = self.GetNeighborWeight()
        for i in range(N):
            self.Update()

    def CreateImage(self, filename):
        """
        根据元胞生成图像
        """
        # driver = gdal.GetDriverByName("GTiff")
        # tods = driver.Create(filename, self.width, self.height, 1, options=["INTERLEAVE=PIXEL"])
        # tods.WriteRaster(0, 0, self.width, self.height, self.cells.tostring(), self.width, self.height)

        # 怎么导入空间参考系统?
        driver = gdal.GetDriverByName("GTiff")
        tods = driver.Create(filename, self.width, self.height, 1, options=["INTERLEAVE=PIXEL"])
        tods.SetGeoTransform(self.geotransform)
        tods.SetProjection(self.projection)
        tods.WriteRaster(0, 0, self.width, self.height, self.cells.tostring(), self.width, self.height)


if __name__ == "__main__":
    os.chdir('F:/CA/cityCA/data/')
    world = LandWorld('gc00.tif', 'gc05.tif', 'DisToTownRoad.tif')
    N = 3
    world.Start(N)
    world.CreateImage('F:/CA/cityCA/中间成果/运行输出/' + 'result' + str(N) + '.tif')

后记

写这篇文章的已经是挺久以前的事了,今天看到别人实现的元胞自动机,好奇别人都是怎么画出栅格的,然后有一个是用C、C++的printf直接打印出来的!!!!原来可以通过打印小格子的方式来实现,我还一直纠结怎么画栅格!!!


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,116评论 25 707
  • <出发> 大学四年的时光匆匆流过,豆豆的学生身份已经成为过去式,有怀念不舍,有遗憾不甘,但都无法从来…四年...
    AmazingBean阅读 243评论 6 5
  • 1、金钱:它让女人经济独立的同时人格独立; 2、学习:它让女人越来越自信,由内而外散发出知性与涵养; 3、容颜:她...
    静待花开jl阅读 231评论 0 0
  • 有位宝妈告诉我:她家宝宝九个多月时还要天天抱着,不怎么会爬,也懒得爬,突然到了近十一个月,就开始学走路了,...
    未央之雨阅读 295评论 0 0