Folium Popup

Folium交互式地图及popup中文显示

本文将介绍使用Folium生成交互式地图并使popup弹框支持中文显示

Folium是一个python包,可以用Python代码非常方便的生成嵌入网页的基于Leaflet的交互式地图,介绍可见主页。最简单的安装方法还是通过pip。

Folium的说明文档不太完整,但是几个例子还是能把功能说明白的,主页中就有。当然,如果出现莫名其妙的bug,就得考验大家的智慧了。在我个人的使用过程中就遇到了一个困扰很久的bug,popup弹框不能显示中文。受python4oceanographers的一篇博文的启发,终于解决,在此特别感谢,并分享给中文世界的朋友们!

本文将通过一个应用场景尝试解决以下问题:

  1. 找到距离某地最近的若干记录(并用公里显示距离)
  2. 显示在Folium地图上(配置合适的标志)
  3. 点击显示详细信息(中文!)

数据预处理

首先还是下载中国气象站点数据,读入pandas并转换成十进制度,上一篇博文有介绍,不再赘述。

In [1]:
import pandas as pd
In [2]:
data = pd.read_excel('SURF_CHN_MUL_HOR_STATION.xlsx')
In [3]:
def min2deg(x):
    y = int(x)
    y = y + (x - y)*1.66666667
    return y

data['经度'] = data['经度'].apply(min2deg)
data['纬度'] = data['纬度'].apply(min2deg)

整理一下数据精确的有效数字

In [4]:
data['观测场拔海高度(米)'] = data['观测场拔海高度(米)'].apply(lambda x:round(x,1))

data['纬度'] = data['纬度'].apply(lambda x:round(x,2))
data['经度'] = data['经度'].apply(lambda x:round(x,2))
In [5]:
data.head(5)
Out[5]:
省份 区站号 站名 纬度 经度 气压传感器拔海高度(米) 观测场拔海高度(米)
0 安徽 58015 砀山 34.45 116.33 45.4 44.2
1 安徽 58016 萧县 34.18 116.97 35.9 34.7
2 安徽 58102 亳州 33.87 115.77 39.2 37.7
3 安徽 58107 临泉 33.02 115.28 37.0 35.8
4 安徽 58108 界首 33.23 115.33 35.0 34.0

将这个版本的数据存起来,以便下次使用

data.to_csv('中国气象站点信息_decimal.csv')

计算两点距离(公里表示)

已知两点经纬度计算两点距离有其公式,详见Movable-Type的介绍(包括JS代码和网页计算器)。以下Python代码来自Bruno Rocha的分享在此感谢!

In [6]:
# Haversine formula example in Python
# Author: Wayne Dyck

import math

def distance(origin, destination):
    lat1, lon1 = origin
    lat2, lon2 = destination
    radius = 6371 # km

    dlat = math.radians(lat2-lat1)
    dlon = math.radians(lon2-lon1)
    a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
        * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    d = radius * c

    return d

赤道上1度约111公里,北纬30度附近1度越96公里:

In [7]:
distance((0,1),(0,0))
Out[7]:
111.19492664455873
In [8]:
distance((30,0),(30,1))
Out[8]:
96.29732567761188

用Harversine函数计算所有记录的距离:

In [9]:
data['距离雄县(km)'] = data.apply(lambda r:distance((r['纬度'],r['经度']),(39.02,116.10)),axis=1)

选择距离雄县50km以内的记录

In [10]:
data[data['距离雄县(km)']<50].sort_values('距离雄县(km)')
Out[10]:
省份 区站号 站名 纬度 经度 气压传感器拔海高度(米) 观测场拔海高度(米) 距离雄县(km)
592 河北 54636 雄县 39.02 116.10 12.2 11.1 0.000000
569 河北 54605 安新 38.93 115.93 5.6 4.4 17.779593
546 河北 54503 容城 39.07 115.82 14.0 12.8 24.811635
552 河北 54518 霸州 39.17 116.40 10.4 8.9 30.797155
574 河北 54610 任丘 38.73 116.10 9.2 8.1 32.246529
576 河北 54612 文安 38.85 116.45 5.4 4.3 35.689976
547 河北 54506 高碑店 39.32 115.95 25.7 24.6 35.777055
565 河北 54601 徐水 38.98 115.65 14.3 13.1 39.140108
567 河北 54603 高阳 38.72 115.77 11.2 10.0 43.920144
553 河北 54519 永清 39.30 116.48 13.4 12.2 45.197136
550 河北 54512 固安 39.42 116.28 22.6 21.4 47.103376

剔除雄县本身后即为要找的50km内的记录:

In [11]:
selected_st = data[data['距离雄县(km)']<50].sort_values('距离雄县(km)').iloc[1:,:]

Folium地图显示

Folium设置初始地图很简单,只需指定一个中心坐标。找到雄县的坐标:

In [12]:
import folium
In [13]:
data.ix[data['站名']=='雄县',['站名','纬度','经度']]
Out[13]:
站名 纬度 经度
592 雄县 39.02 116.1
In [14]:
xmap = folium.Map(location=[39.0,116.1])
In [15]:
xmap
Out[15]:

在雄县这个位置添加一个标记,设置标记的图形样式:

这个icon的设置很有趣,可以变化出很多用途,Folium.Icon类的介绍可以通过在jupyter notebook里执行Folium.Icon?显示。

简单说这个类可以设置color, icon_color, icon, angle, prefix这5个参数。color的可选项包括:['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'lightred', 'beige', 'darkblue', 'darkgreen', 'cadetblue', 'darkpurple', 'white', 'pink', 'lightblue', 'lightgreen', 'gray', 'black', 'lightgray'] ,或者HTML颜色代码;icon_color同上;icon可以在Font-Awesome网站中找到对应的名字,并设置prefix参数为’fa';最后,angle以度为单位设置。

In [16]:
icon_kw = dict(prefix='fa', color='orange', icon_color='darkred', icon='cny')
icon = folium.Icon(**icon_kw)

创建一个Marker对象,然后加入xmap地图中,注意,这里popup我直接写上了汉字“雄县”:

In [17]:
folium.Marker(
    location=[39.0,116.1],
    popup='雄县',
    icon=icon
).add_to(xmap);
In [18]:
xmap
Out[18]:

点击标志,发现显示的是乱码,其原因在于这里popup只能使用ascii编码。

中文显示

我们不妨先看一下系统默认编码是什么:

In [19]:
import sys
print(sys.getdefaultencoding())
utf-8

Folium自带一个IFrame类可以在其中嵌入html代码并解释,因此给了我们一个机会借道HTML来实现中文显示。那么首先要把汉字编码为ascii xml字符的形式。经过多次试验,以下函数可以实现正确显示:

In [20]:
def utf2asc(s):
    return str(str(s).encode('ascii', 'xmlcharrefreplace'))[2:-1]
In [21]:
utf2asc('雄县')
Out[21]:
'&#38596;&#21439;'

写一个最简单的html标记:

In [22]:
heading3 = """<h3>{}</h3>""".format
In [23]:
heading3(utf2asc('雄县'))
Out[23]:
'<h3>&#38596;&#21439;</h3>'
In [24]:
from folium.element import IFrame

iframe = IFrame(html=heading3(utf2asc('雄县¥')),width=100,height=50)

加上一个¥以测试对中文符号的显示能力

In [25]:
popup = folium.Popup(iframe)
In [26]:
xmap = folium.Map(location=[39.0,116.1])

folium.Marker(
    location=[39.0,116.1],
    popup=popup,
    icon=icon
).add_to(xmap);

xmap
Out[26]:

此时popup可以正确显示中文及符号

popup美化

借鉴Filipe的博文中的HTML代码以美化popup,使其呈现表格:

列名也需要使用编码映射:

In [27]:
{k:utf2asc(k) for k in selected_st.columns.tolist()}
Out[27]:
{'区站号': '&#21306;&#31449;&#21495;',
 '气压传感器拔海高度(米)': '&#27668;&#21387;&#20256;&#24863;&#22120;&#25300;&#28023;&#39640;&#24230;&#65288;&#31859;&#65289;',
 '省份': '&#30465;&#20221;',
 '站名': '&#31449;&#21517;',
 '纬度': '&#32428;&#24230;',
 '经度': '&#32463;&#24230;',
 '观测场拔海高度(米)': '&#35266;&#27979;&#22330;&#25300;&#28023;&#39640;&#24230;&#65288;&#31859;&#65289;',
 '距离雄县(km)': '&#36317;&#31163;&#38596;&#21439;(km)'}

popup中的HTML,以站名、站号、观测场海拔的顺序构建列表,注意看<tr>部分

In [28]:
table = """
<!DOCTYPE html>
<html>
<head>
<style>
table {{
    width:100%;
}}
table, th, td {{
    border: 1px solid black;
    border-collapse: collapse;
}}
th, td {{
    padding: 5px;
    text-align: left;
}}
table#t01 tr:nth-child(odd) {{
    background-color: #eee;
}}
table#t01 tr:nth-child(even) {{
   background-color:#fff;
}}
</style>
</head>
<body>

<table id="t01">
  <tr>
    <td>&#31449;&#21517;</td>
    <td>{}</td>
  </tr>
  <tr>
    <td>&#21306;&#31449;&#21495;</td>
    <td>{}</td>
  </tr>
  <tr>
    <td>&#35266;&#27979;&#22330;&#25300;&#28023;&#39640;&#24230;&#65288;&#31859;&#65289;</td>
    <td>{}</td>
  </tr>
</table>
</body>
</html>
""".format

循环列表各行,将信息添加进地图:

In [29]:
for k,v in selected_st.iterrows():
    
    iframe = IFrame(html=table(utf2asc(v['站名']),utf2asc(v['区站号']),utf2asc(v['观测场拔海高度(米)']))
                    ,width=310,height=130)
    popup = folium.Popup(iframe,max_width=400)
    
    folium.Marker(
        location=[v.纬度,v.经度],
        popup=popup
        ).add_to(xmap);
In [30]:
xmap
Out[30]:
In [ ]:
 

各种格式地理信息数据读取和显示

各种格式地理信息数据读取和显示

常见空间数据格式包括:文本xy坐标(包括.dat, .csv, .xls, .txt)、.shp、GeoJson、图像(包括GeoTiff等)、netCDF。以上格式数据在Python中皆可处理,不过可能需要用到多个不同的工具包,本文将介绍作者所熟悉的几种除GDAL外的Python包供读者探讨。

Macbook Pro 2011b, OSX,python3.5

矢量数据类

xy坐标,气象站点的分布

本节介绍如何使用Pandas读取及简单处理文本文件存储的点坐标,并使用基于Matplotlib的CartoPy绘制地图,力图用最简单的方法来解决一些常见的问题。pandas用来处理文本表格数据非常舒服,推荐Mckinney著,机械工业出版社的《利用Python进行数据分析》。

  • 建议使用conda安装方式,如安装CartoPy遇见麻烦,可以通过conda-forge安装,执行代码: conda install cartopy -c conda-forge
  • 如果认为Anaconda太大,很多内置的包用不上,可参考使用Miniconda,一键安装脚本可参考Nicola的博客

全国气象站点空间信息(Excel .xlsx格式,包括名称、坐标、海拔高度)可从中国气象数据网下载,点击获取

In [1]:
import pandas as pd

如果用pandas读Excel文件,需要事先安装xlrd包,conda install xlrd即可

In [39]:
data = pd.read_excel('SURF_CHN_MUL_HOR_STATION.xlsx')
In [4]:
data.head()
Out[4]:
省份 区站号 站名 纬度 经度 气压传感器拔海高度(米) 观测场拔海高度(米)
0 安徽 58015 砀山 34.27 116.20 45.4 44.200001
1 安徽 58016 萧县 34.11 116.58 35.9 34.700001
2 安徽 58102 亳州 33.52 115.46 39.2 37.700000
3 安徽 58107 临泉 33.01 115.17 37.0 35.800000
4 安徽 58108 界首 33.14 115.20 35.0 34.000000

pandas的选择操作很灵活,在此依次展示:

  1. 挑选站名为“北京”的记录
  2. 省份为西藏的记录数
  3. 观测场海拔高度最高的5个站
In [5]:
data.ix[data['站名']=='北京',:]
Out[5]:
省份 区站号 站名 纬度 经度 气压传感器拔海高度(米) 观测场拔海高度(米)
79 北京 54511 北京 39.48 116.28 32.5 31.3
In [18]:
data.ix[data['省份']=='西藏',:].shape[0]
Out[18]:
23

如下,通过==选择得到的True/False列,True为1,False为0,所以此列之和也是所选得到的记录数目

In [19]:
(data['省份']=='西藏').sum()
Out[19]:
23
In [24]:
data.sort_values('观测场拔海高度(米)',ascending=False).head(5)
Out[24]:
省份 区站号 站名 纬度 经度 气压传感器拔海高度(米) 观测场拔海高度(米)
1371 青海 52908 五道梁 35.13 93.05 4613.0 4612.2
1380 青海 56004 沱沱河 34.13 92.26 4533.9 4533.1
1869 西藏 55299 那曲 31.29 92.04 4508.2 4507.0
1386 青海 56034 清水河 33.48 97.08 4416.2 4415.4
1868 西藏 55248 改则 32.09 84.25 4416.1 4414.9

不能肯定经纬度是十进制度格式的还是度.分格式的,那么来验证一下经纬度小数点后的两位是否全都不超过60

In [36]:
data['经度'].apply(lambda x: x-int(x)).sort_values(ascending=False).head()
Out[36]:
1560    0.59
425     0.59
1150    0.59
27      0.59
1295    0.59
Name: 经度, dtype: float64

很显然,经纬度是度.分格式存储的,为方便计算,我们将其转换成十进制度。首先定义一个计算十进制度的函数:

In [42]:
def min2deg(x):
    y = int(x)
    y = y + (x - y)*1.66666667
    return y
In [46]:
data['经度'] = data['经度'].apply(min2deg)
In [47]:
data['纬度'] = data['纬度'].apply(min2deg)
In [50]:
data[['站名','经度','纬度']].head()
Out[50]:
站名 经度 纬度
0 砀山 116.333333 34.450000
1 萧县 116.966667 34.183333
2 亳州 115.766667 33.866667
3 临泉 115.283333 33.016667
4 界首 115.333333 33.233333

有了点坐标,我们现在将他们绘制到地图上,需要用到Matplotlib和CartoPy,先画一个简单的中国地图,并点上所有坐标点。

In [51]:
%matplotlib inline
In [52]:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
In [291]:
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([70, 140, 15, 55])
ax.stock_img();

ax.scatter(data['经度'],data['纬度'],s=0.3,c='g');

此外我们还可以对这些点做一定的统计分析,比如看一下海拔分布:

In [288]:
plt.figure(figsize=(4,3))
ax = data['气压传感器拔海高度(米)'].plot(kind='hist');
ax.set_xlim([0,5000])
plt.xlabel('elevation (m)');

ESRI Shapefile,中亚地区国家

ESRI .shp文件可以使用Cartopy显示,空间查询可使用Shapely。在此本文使用Natural Earth的1:1000万世界国家数据点击下载,演示shapefile格式数据的显示和查询。

In [156]:
import cartopy.io.shapereader as shpreader
In [157]:
reader = shpreader.Reader('ne_10m_admin_0_countries.shp')

Reader.records()返回的是一个生成器,可以参考函数式编程的各种方法,比如map, reduce, filter, etc.。

In [180]:
countries = reader.records()

可以用next(countries).attributes来查看第一个记录都有哪些属性,注意,可以把生成器的next()方法理解成一个游标,一行一行往下走,那么如果想重新循环的话就要重置一下,countries = reader.records()

In [181]:
c_list = [country for country in countries if country.attributes['ADMIN']=='China']
countries = reader.records()

当然,中国只有一个:

In [182]:
len(c_list)
Out[182]:
1

看一下中亚国家有哪些。首先我们可以先封装一下函数,以便下次使用:

In [187]:
def get_record(key,value):
    countries = reader.records()
    result = [country for country in countries if country.attributes[key]==value]
    countries = reader.records()
    return result
In [204]:
cas = get_record('SUBREGION','Central Asia')
In [205]:
[r.attributes['ADMIN'] for r in cas]
Out[205]:
['Baykonur Cosmodrome',
 'Kazakhstan',
 'Kyrgyzstan',
 'Tajikistan',
 'Turkmenistan',
 'Uzbekistan']

原来,位于哈萨克斯坦的拜科努尔航天发射场也被单独作为一条记录,我们将其删去:

In [206]:
cas.pop(0);

每一条记录都由geometry和attributes组成,将列表中所有记录的geometry循环添加到图上即可生成地图。这里我们更进一步把地名用text()函数表在图上。

In [227]:
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([45,90,35,55])
for r in cas:
    ax.add_geometries(r.geometry,ccrs.PlateCarree(),
                      facecolor='green',edgecolor='black',alpha=0.2,linewidth=0.5)
    ax.text(r.geometry.centroid.x,r.geometry.centroid.y,r.attributes['ADMIN'],
            horizontalalignment='center',
            verticalalignment='center',
            transform=ccrs.Geodetic())

现在我们来比较一下中亚五国人口和经济,此外,用经纬度网格对地图修饰以方便阅读

首先需要用cm将属性值和颜色做一个对应,我们首先做一个最大最小值标准化:

$Y_i = \frac{X_i - X_{min}}{X_{max} - X_{min}}$

In [251]:
from matplotlib import cm
In [255]:
pop = [r.attributes['POP_EST'] for r in cas]

pop_min = min(pop)
pop_max = max(pop)
In [270]:
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([45,90,35,55])

for r in cas:
    
    color = cm.Greens((r.attributes['POP_EST'] - pop_min) / (pop_max - pop_min))
    
    ax.add_geometries(r.geometry,ccrs.PlateCarree(),
                      facecolor=color,edgecolor='black',linewidth=0.5)
    ax.text(r.geometry.centroid.x,r.geometry.centroid.y,r.attributes['ADMIN'],
            horizontalalignment='center',
            verticalalignment='center',
            transform=ccrs.Geodetic())
sm = plt.cm.ScalarMappable(cmap='Greens', norm=plt.Normalize(vmin=pop_min, vmax=pop_max))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm); 
plt.title('Population estimates');

看看经济情况,在加上经纬度标

In [271]:
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
In [276]:
gdp = [r.attributes['GDP_MD_EST'] for r in cas]

gdp_min = min(gdp)
gdp_max = max(gdp)

ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([45,90,35,55])

for r in cas:
    
    color = cm.Greens((r.attributes['GDP_MD_EST'] - gdp_min) / (gdp_max - gdp_min))
    
    ax.add_geometries(r.geometry,ccrs.PlateCarree(),
                      facecolor=color,edgecolor='black',linewidth=0.5)
    ax.text(r.geometry.centroid.x,r.geometry.centroid.y,r.attributes['ADMIN'],
            horizontalalignment='center',
            verticalalignment='center',
            transform=ccrs.Geodetic())
    
ax.set_xticks([45,55,65,75,85], crs=ccrs.PlateCarree())
ax.set_yticks([35,45,55], crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter(zero_direction_label=True)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)

plt.title('GDP estimates');
GeoJson格式

这个格式跟json没差别,使用起来只要跟shapely相互转换就行了,不过强烈建议使用geopandas处理shapefile和geojson的读写,因为geopandas基于Fiona,较shapely强大。Geopandas的使用方式超出本文范围,将在随后的博文中介绍。

python4oceanographers有一个博文,提供了Shapely records和GeoJson的相互转换函数,可以点此查看,文中转换geojson的主要目的是便于交互式地图Folium的显示。

学习资源

本文更新于:2016-11-30

​ 中文网络世界中有很多关于使用Python进行数据分析的教程和博文,但是作为一名研究气候变化方向的博士生,我还没有找到较完美的介绍使用Python分析气候数据的中文资源。于是,我自己开了这个博客,分享一下在气候变化领域里使用Python的一些技术文章。这个博客中的所有内容都将会是我的原创而不会有转载。值得分享的文章我会在融入到我每篇文章的引用中,请读者注意点击链接浏览。如果发现链接失效,请及时跟我联系。

​ 这篇文章是我整理自己的学习过程写出的,会不定期更新。可以在此保证,所有内容都是我认真阅读过,并认为值得推荐的。我相信很多朋友也会推荐其它的资源,所以也非常希望在这里能有一个跟大家交流的机会。

​ 使用Python进行气候数据分析的相关技术更新很快,本博客所列内容大部分为英文,如果希望某篇感兴趣的文章被翻译成中文,可以与我联系,我愿意在业余时间联系原创作者并在获得允许后翻译。

​ Email: yafei.li[at]mail.bnu.edu.cn

书籍

  1. 作为Python和numpy学习的入门,强力推荐《利用Python进行数据分析》附录部分。把这本书看完,Pandas也了解透了,所以数据处理的能力就没问题了。
  2. 如果想使用Python进行机器学习或数据挖掘相关工作,推荐看中文的《Python数据分析与挖掘实战》,更深入一点点的话推荐英文的Introduction to Machine Learning with Python: A Guide for Data Scientists,这本书很新,我在2016年7月参加scipy2016会议的时候两位作者做了非常精彩的Tutorial。

网络资源

earthpy,研究地球科学的学者使用Python分享的经验,有些过时,不过很有用。

Damine的博客。非常适合初学者。作者是以为气候研究的年轻学者,他使用各种开源工具的经验被Nature参访过。介绍了一个想用Python的科学家如何入门Python和各种工具。

  • 最近这篇,介绍了各种有用的包,类似于一个路线图,帮你找到你现在需要用的包。

Nikolas的博客,有几篇写的很好文章,比如以下所列,但是最近好像停止更新了。

UEA-东英吉利亚大学学生们交流Python使用的经验的会议日志,定期更新,很多内容适合初学者。

使用Nikola建立个人网站

本文最近更新于: 2016-11-30

  • Jupyter notebook是一款非常好用的编辑器。它最大的优点在于可以在浏览器中编辑代码并即时显示出图表。
  • Nikola是一款基于Python的静态网页生成器
  • GitHub是托管代码网站。此外,GtiHub还可用于发布Nikola生成的网站。

安装 Jupyter Notebook

首先确定你的操作系统,并下载相应的 Miniconda 并安装。

安装完成后,在终端输入 conda --version 将会显示conda的版本。

现在,可以使用 conda 安装相应组件,例如ipython-notebook。

In [ ]:
conda install ipython-notebook

使用Jupyter Notebook,在终端输入:

In [ ]:
jupyter notebook

安装 Nikola

建议使用conda 创建一个虚拟的环境,以方便管理:

(此处YOUR_ENV_NAME是自己指定的名字)

In [ ]:
conda create --name YOUR_ENV_NAME python=3

激活刚刚创建的conda环境,在终端中输入:

In [ ]:
source activate YOUR_ENV_NAME

使用pip安装:

(miniconda 中自带了pip,无需重新安装)

In [ ]:
pip install nikola

初始化站点

首先使用cd, mkdir新建一个文件夹用于存放站点资源。

假设我们目前已经在站点文件夹的目录下,初始化:

(注意代码最后有一个点,代表当前目录)

In [ ]:
nikola init .

初始化后,将会在目录中看到一个conf.py的文件。

使用文本编辑器打开conf.py,并在如下两处编辑:(文字很多,建议使用搜索)

In [ ]:
POSTS = (
    ("posts/*.ipynb", "blog", "post.tmpl"),
    ("posts/*.md", "blog", "post.tmpl"),
    ("posts/*.rst", "blog", "post.tmpl"),
    ("posts/*.txt", "blog", "post.tmpl"),
)
PAGES = (
    ("pages/*.md", "", "story.tmpl"),
    ("pages/*.ipynb", "", "story.tmpl"),
    ("pages/*.rst", "", "story.tmpl"),
    ("pages/*.txt", "", "story.tmpl"),
)

这样做的目的是让你以默认jupyter notebook的格式发布新博文

初始化Git

还是在站点的目录下,初始化git:

In [ ]:
git init
git add *
git commit -am "Initial commit"

你还需要在GitHub上注册一个账号,然后创建一个项目库(repository).

将站点目录和内容推送到GitHub的服务器上:

In [ ]:
git remote add origin https://github.com/USERNAME/PROJECTNAME.git
git push origin

USERNAME和PROJECTNAME依然需要你自己定义,例如,这个博客,我的GitHub的username就是yafei777, projectname就是Yafei-blog

写文章

我们可以先写一个jupyter notebook格式的博文:

In [ ]:
nikola new_post -f ipynb

启动一个jupyter notebook来编辑刚刚创建的空文章:

In [ ]:
jupyter notebook posts

写好保存后,让nikola生成静态页面,并显示到浏览器上:

In [ ]:
nikola auto

注意! 如果遇到提示什么有某个python包没有安装,只需 pip install PACKAGE 就可以了, 此处的PACKAGE是提示缺少的包名。

发布到GitHub Pages上

GitHub pages服务是免费的,此时也先不需要另外购买域名。当然你也可以发布到其它服务器上,没有问题。

打开文本编辑器编辑conf.py:

In [ ]:
GITHUB_SOURCE_BRANCH = 'master'
GITHUB_DEPLOY_BRANCH = 'gh-pages'
GITHUB_REMOTE_NAME = 'origin'

好了,现在可以让nikola自动发布到GitHub上去了,只需在终端中输入:

In [ ]:
nikola github_deploy

完成之后应该就可以在 https://USERNAME.github.io/PROJECTNAME 中看到了创建的网站和第一篇文章了!

以后发布文章的操作流程

以发布Markdown文件为例

  1. 打开终端(Terminal),激活环境:source activate nikola,如果是win系统:activate nikola。此处,nikola是我conda环境的名字,参考上文。
  2. 转到网站目录,新建博文:nikola new_post -f markdown
  3. ./posts/目录下打开上一步创建的文件并修改。
  4. 编译:nikola build
  5. 发布到GitHub:nikola github_deploy

发布ipython notebook文件,可以在Notebook里插入Metadata:

Edit -> Edit Notebook Metadata

"nikola": { "date": "2016-11-21 12:27:12 UTC+08:00", "title": "使用Nikola建立个人网站", "description": "", "link": "", "type": "text", "category": "", "slug": "jupyter-nikola-and-github", "tags": "" },