以前在處理gis資料的時候,都是直接導入本地shp素材、本地geojson素材,本地topojson素材,自從接觸postgis資料之後,深感使用規範的存儲系統來統一管理gis資料的好處,特别是資料量大了之後,優勢便更加明顯,你可以選擇将很多需要做空間計算的步驟轉移到Postgis資料庫内進行計算,要知道Postgis提供的空間計算能力與R和Python這種應用導向的工具相比,優勢要大得多。
在批量導入素材之前,我們可以先看下R語言目前提供的各種導入接口在I/O性能上相比有何異同。
#install.packages("geojsonio")
#devtools::install_github("ropensci/geojsonio")
library("geojsonio")
library("rgdal")
library("sf")
library("maptools")
使用maptools包中的readShapePoly函數進行導入(已快被遺棄了,推薦使用sf和rgdal包)
system.time(china_map <- readShapePoly("D:/R/rstudy/CHN_adm/bou2_4p.shp"))
使用者 系統 流逝
0.23 0.00 0.23
Warning message:
use rgdal::readOGR or sf::st_read
china_map@data
ggplot2::fortify(china_map)
geojsonio包導入:
system.time(geojson1 <- geojson_read(
"D:/R/rstudy/CHN_adm/bou2_4p.shp",
method = "local",
parse = TRUE,
what = "sp",
encoding="utf-8",
use_iconv=TRUE
))
使用者 系統 流逝
0.69 0.03 0.71
使用rgdal包:
system.time(map_data <- readOGR(
"D:/R/rstudy/CHN_adm/bou2_4p.shp",
encoding="utf-8",
use_iconv=TRUE
))
OGR data source with driver: ESRI Shapefile
Source: "D:\R\rstudy\CHN_adm\bou2_4p.shp",
layer: "bou2_4p"with 925 features
It has 7 fields
Integer64 fields read as strings: BOU2_4M_ BOU2_4M_ID
使用者 系統 流逝
0.66 0.09 0.75
使用sf包導入:
system.time(nepal_shp <- read_sf(
"D:/R/rstudy/CHN_adm/bou2_4p.shp",
options = "ENCODING=gbk"
))
使用者 系統 流逝
0.05 0.00 0.05
可以看到在同一個shp檔案單項導入的情況下,純粹從時間上來看:
sf > maptools > rgdal > geojsonio
這裡值得一提的是,geojsonio包是封裝的rgdal服務,性能上自然略遜rgdal一籌,以上四個包中,除sf包是基于simple features标準的模型之外,其他基本都是基于sp模型的。sf模型的性能由此可見一斑。
當然,以上sf包、rgdal包和sf包都是相容性很好地包,可以支援非常廣泛的資料源,以下分别是在json标準下的兩種素材上進行測試。
geojson
system.time(geojson <- geojson_read(
"D:/R/mapdata/State/china.geojson",
method = "local",
parse = TRUE,
encoding="utf-8",
use_iconv=TRUE,
what = "sp"
))
使用者 系統 流逝
0.80 0.02 0.81
system.time(map_data <- readOGR(
"D:/R/mapdata/State/china.geojson",
encoding="utf-8",
use_iconv=TRUE,
stringsAsFactors = FALSE
))
OGR data source with driver: GeoJSON
Source: "D:\R\mapdata\State\china.geojson", layer: "china"with 34 features
It has 2 fields
使用者 系統 流逝
0.77 0.00 0.76
system.time(nepal_shp <- read_sf(
"D:/R/mapdata/State/china.geojson"
))
使用者 系統 流逝
0.03 0.00 0.03
topojson
system.time(map_data <- readOGR(
"D:/R/mapdata/china.topojson",
use_iconv=TRUE,
encoding = "utf-8",
stringsAsFactors = FALSE
))
OGR data source with driver: GeoJSON
Source: "D:\R\mapdata\china.topojson", layer: "china"with 34 features
It has 2 fields
使用者 系統 流逝
0.52 0.01 0.59
system.time(geojson <- topojson_read(
"D:/R/mapdata/china.topojson",
encoding="utf-8",
use_iconv=TRUE
))
OGR data source with driver: GeoJSON
Source: "D:\R\mapdata\china.topojson", layer: "china"with 34 features
It has 2 fields
使用者 系統 流逝
0.59 0.00 0.59
system.time(nepal_shp <- read_sf(
"D:/R/mapdata/china.topojson"
))
使用者 系統 流逝
0.02 0.00 0.01
是不是看完這個性能大比拼之後大吃一驚,為sf包的超強IO能力所折服,sf包是一個非常強大的包,實作了基于simple features的所有特性,如果你了解一點兒Postgis的話,你會發現作者把大部分空間運算的函數名稱設計的和Postgis中的函數一模一樣,這就意味着你無論是隻了解過sf包函數,或者隻了解過Postgis函數,都可以低成本的遷移到兩一個平台,因為同名函數往往功能一緻。
如果你要想将sf包導入的資料模型轉換為普通的資料框模式,僅僅隻需使用其提供的as(sf,’Spatial’)函數一次轉化即可,當然sf有自己的ggplot2通道函數geom_sf(),這意味着你不必多此一舉。(當然對于sf不甚熟悉,習慣于使用geom_polygon來實作地理資訊可視化的小夥伴兒,可以采取這種辦法,但是仍然要推薦大家學習sf包,因為它代表着未來)。
R語言-gis資料批量入庫:
#定義讀寫函數:
task <- function(filename,conn){
#此處為寫入本地gis資料(可以是任意格式,可以使用任意一種導入工具)
map_data <- readOGR(filename,use_iconv=TRUE,encoding = "utf-8",stringsAsFactors = FALSE)
file_name <- sub('.json','',basename(filename))
#此處是寫入資料庫的函數,可以使用sf包、rgdal包以及RPostgreSQL包提供的寫出函數。
writeOGR(obj = map_data ,dsn = conn,driver = "PostgreSQL",layer=file_name,encoding="gbk",overwrite_layer = TRUE)
}
#此處使用l_ply函數建立批量執行任務
Project_io <- function(path){
setwd(path)
input_list = list.files(path)
conn <- "PG:dbname='mytest' host='localhost' port='5432' user='postgres' password='708965'"
l_ply(input_list,task,conn)
}
#啟動任務
Project_io("D:/R/mapdata/Province")
Python-gis資料批量入庫:
import geopandas as gpd
import pandas as pd
from sqlalchemy import create_engine
from geoalchemy2 import Geometry,WKTElement
import numpy as np
import os
import re
import json
#資料寫入函數:
def write_gis(path):
map_data = gpd.GeoDataFrame.from_file(path)
map_data['geometry'] = map_data['geometry'].apply(lambda x: WKTElement(x.wkt,4326))
map_data.drop(['center','parent'], axis = 1, inplace=True)
map_data.to_sql(
name = re.split('\\.',path)[0],
con = engine,
if_exists= 'replace',
dtype = {'geometry':Geometry(geometry_type ='POLYGON',srid = 4326)}
)
return None
#建立批量任務
def to_do(file_path,username,password,dbname):
os.chdir(file_path)
link = "postgresql://{0}:{1}@localhost:5432/{2}".format(username,password,dbname)
engine = create_engine(link,encoding = 'utf-8')
file_list = os.listdir()
map(lambda x: write_gis(x),file_list)
return None
#執行任務計劃
if __name__ == '__main__':
file_path = 'D:/R/mapdata/Province'
username = 'postgres'
password = *****
dbname = 'mytest'
to_do(file_path,username,password,dbname)
print('DODE')
原文釋出時間為:2018-08-03
本文作者: 杜雨
本文來自雲栖社群合作夥伴“
資料小魔方”,了解相關資訊可以關注“
”