Leetao's Blog

Talk is cheap, show me the code

0%

最近想重构一下自己的代码,仔细查看了自己写的代码,发现自己写的两个类,其中一个类是对 Gis 信息的处理展示,另外一个类是生成符合 Highcharts 格式的数据,两个类的功能十分不同,而且在 继承层次结构 中没有 共同的父类。但是两个类在生成数据之前,都必须对前台传来的指标名称进行转换成相应的指标 id,要怎么解决这个问题呢?

  1. 创建一个父类让这两个类都继承
  2. 创建一个接口,定义实现转换指标的的方法,然后让这两个类都实现它
  3. 创建一个性状,定义实现指标转换的方法,然后让这两个类混入这个性状

第一种解决方法不好,因为这样子做会强制让两个无关的类继承同一个祖先,而且这个祖先不属于各自的继承层次结。
第二种解决方法好,因为每个类都能保有自然的继承层次结构。但是,我们要在两个类中重复实现的地理编码功能,这不符合 DRY (Don’t Repeat Yourself ) 原则。
第三种解决方法最好,这么做不会搅乱这两个类原本自然的继承层次结构。

自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承

性状有两个作用:

  1. 表明类可以做什么( 像是接口 )
  2. 提供模块化实现 ( 像是类 )

性状能把模块化的实现方法注入到多个无关的类中。而且性状还能促进代码重用。

性状的创建

1
2
3
4
5
6
7
trait KeyHandle
{
public function tranKeyNameToKeyId($keyname)
{
// 将 keyname => keyid
}
}

性状的使用

1
2
3
4
5
6
7
8
9
class Gis
{
use KeyHandle;
// 类的具体实现
public funcition getGisByKeyId($keyid)
{

}
}

性状的优先级

从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了trait的方法,而 trait 则覆盖了被继承的方法。

例子

可运行示例

trait 则覆盖了被继承的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  Base  {
public function sayHello () {
echo 'Hello ' ;
}
}

trait SayWorld {
public function sayHello () {
parent :: sayHello ();
echo 'World!' ;
}
}

class MyHelloWorld extends Base {
use SayWorld ;
}

$o = new MyHelloWorld ();
$o -> sayHello ();

输出:

1
Hello World!

当前类的成员覆盖了 trait 的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}

class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

输出:

1
Hello Universe!

项目示例

1
2
3
$gis = new Gis();
$keyid = $gis->tranKeyNameToKeyId($keyname);
return $gis->getGisByKeyId($keyid);

|
| code | 必需,要推迟执行的函数名或者一段js代码 |
| millisec | 必须,推迟执行的毫秒数 |
| lang | 可选,JScript/VBScript/JavaScript|

Talk is cheep.show me the code~~看下面一段代码

1
2
3
4
5
6
7
function func() {
// code1
setTimeout(function(){
// code3
},1000);
// code2
}

在讲述这段代码之前,我们首先还得明确一件事情:
js任务分为同步任务和异步任务,只有当主线程空闲,才会去读取“任务队列”,不幸的是,异步任务就是进入到“任务队列”的任务,只有当“任务列队”通知主线程,某个异步任务可以执行了并且同步任务执行完毕,该任务才会进入主线程执行。额外补充这些知识,之后再往下看。

假设该函数被执行了,首先执行到 code1 的内容,然后运行到 setTimeout 部分,setTimeout 这个时候告诉浏览器,我这里有段代码将会在 1000 ms之后插入到你的任务队列当中去,然后浏览器主线程接着执行了 code2,等到了 1000ms,会出现什么情况呢,这个时候定时器将 code3 插入到了任务队列,这并不意味着code3就会被立即执行,如果 code2 的执行时间超过了 1000ms,那么这个时候很显然 code3不会被执行,需要等待 code2 执行完毕,当然如果 code2 的执行时间不超过 1000ms,那么到了1000ms,code3就会被执行了,这个时候定时器就正常发挥作用了。

把上面的代码改一下:

1
2
3
4
5
6
7
function func() {
// code1
setInterval(function(){
// code3
},1000);
// code2
}

对于这段代码可能会出现三种情况:

  1. 正常执行
  2. 时间间隔可能会跳过
  3. 时间间隔可能小于定时调用的代码的执行时间

正常的情况就不多说了,说说第二种可能会被跳过的原因,整个函数执行下来要1500ms,假设 code1 在200ms执行完成,以此作为时间点,setInterval将在1200ms时将code3插入到任务队列当中,然后func()函数顺利执行,不幸的是,code3代码执行的时间比较长,到下个 时间点2200ms时又插入了一份code3,你觉得接下来的时间点中,还会再插入新的code3吗~~实际上并不会,所以3200ms这个时间点会被跳过
为了避免这种情况,可以使用如下的代码:

1
2
3
4
setTimeout(function(){ 
//processing
setTimeout(arguments.callee, interval);
}, interval);

arguments.callee表示引用当前正在执行的函数,或者说是调用arguments.callee的函数对象的引用,它给匿名函数提供了一种自我引用的方式。

整理翻译自How To Structure Large Flask Applications

介绍

其实已经有许多关于如何组织架构 Python Web 应用的方法和约定。尽管现在有些框架本身承载了一些工具(脚手架)从而可以达到实现自动化和降低任务难度的效果,但是究根结底几乎所有的解决方案都是依赖于打包/模块化应用作为代码块从而达到分发到相关的文件和文件夹的目的。

最小的 web 应用开发框架 Flask 有着它特有的(工具)-blueprints。

在这篇数码海洋的文章里,我们将会了解到如何使用 Flask 的 blueprints 来创建一个应用目录和组织架构它从而使其变成可重用的组件。这些将会给你的维护和组件的开发带来巨大的好处和便捷。

学术表

  1. Flask:最小的应用开发框架
  2. 关于我们在这篇文章的选择
  3. 准备Flask系统
    1. 准备操作系统
    2. 安装Python,pip以及virtualenv
  4. 组织架构应用的目录
    1. 创建应用目录
    2. 创建虚拟环境
    3. 创建应用文件
    4. 安装 Flask
  5. 使用模块和 Blueprints(组件)
    1. 模块基础
    2. 模块模板
  6. 创建应用(run.py,init.py 等等)
    1. 使用 nano 编辑 run.py
    2. 使用 nano 编辑 config.py
  7. 创建一个模块/组件
    1. 组织架构模型
    2. 定义模块数据类型
    3. 定义模块表单
    4. 定义应用控制器(views)
    5. 在 “app/init.py” 中建立应用
    6. 创建模板
    7. 实际操作中查看你的模型

Flask:最小的应用开发框架

Flask 迷你的框架但是丝毫不会影响重要事情的处理方式。相反, Flask 允许开发者使用他们渴望的并且熟悉的工具。出于这个目的,它由此产生了自己的扩展索引和大量已经存在并解决大多数事情的的工具从登陆到日志方方面面。

它不是一个严格的“常规”框架,部分依赖于配置文件,这些配置文件使得其在开始使用和保持事项时变得更容易。

我们在文章中的选择

通过我们前面浏览的内容了解到,Flask 的方式就是让我们使用那些用起来最舒服的工具。在我们的文章中,我们将会使用最常见的扩展和库。这些选择包括:

  1. SQLAlchemy
  2. WTForms

Flask-SQLAlchemy

在 Flask 中添加 SQLAlchemy 支持很快捷。这是一个很好的扩展。

1
2
3
4
Author: Armin Ronacher
PyPI Page: Flask-SQLAlchemy
Documentation: Read docs @ packages.python.org
On Github: [mitsuhiko/flask-sqlalchemy](https://github.com/mitsuhiko/flask-sqlalchemy)

Flask-WTF

Flask-WTF 提供简单的 WTForms 集成。集成包括可选择的 用来提供更好安全保障的 CSRF。这也是一个很好的扩展。

1
2
3
4
Author: Anthony Ford (created by Dan Jacob)
PyPI Page: Flask-WTF
Documentation: Read docs @ packages.python.org
On Github: [ajford/flask-wtf](https://github.com/mitsuhiko/flask-wtf)

准备 Flask 系统

准备操作系统

使用如下命令安装必要的开发工具

1
2
3
aptitude update
aptitude -y upgrade
aptitude install -y build-essential python-dev python2.7-dev

安装python,pip以及virtualenv

参考 Flask入门——从无到有构建一个网站 (二)环境搭建

组织应用的目录

我们将使用典型的大型应用名称作为我们的应用的文件夹。在文件夹里,我们将会有一个虚拟环境与此同时还有应用的包以及一些其他的文件夹包括 用来运行 测试(开发)环境的 “run.py” 和 Flask 的配置文件 “config.py”

下面给出的作为样例的目录结构,该目录结构是高度可扩展的,它构建的目的是充分利用 Flask 和其他库提供的工具。不要被它吓着了,我们将会一步一步构建它直至完成。

目标样例结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
~/LargeApp
|-- run.py
|-- config.py
|__ /env # Virtual Environment
|__ /app # Our Application Module
|-- __init__.py
|-- /module_one
|-- __init__.py
|-- controllers.py
|-- models.py
|__ /templates
|__ /module_one
|-- hello.html
|__ /static
|__ ..
|__ .
|__ ..
|__ .

创建应用文件夹

让我们开始创建我们需要的主要目录。
逐条去执行下述命令:

1
2
3
4
mkdir ~/LargeApp
mkdir ~/LargeApp/app
mkdir ~/LargeApp/app/templates
mkdir ~/LargeApp/app/static

执行完成之后我们的当前目录

1
2
3
4
~/LargeApp
|__ /app # Our Application Module
|__ /templates
|__ /static

创建一个虚拟环境

使用一个虚拟环境能给你带来很多好处。对于你的每一个应用都强烈推荐使用一个新的虚拟环境。在你的应用中创建一个虚拟环境是一个很好的让你应用有序和干净的方法。

使用下列命令创建一个虚拟环境

1
2
cd ~/LargetApp
virtualenv env

创建应用文件

在这步,我们将会在使用 modules 和 blueprints 之前创建我们基础应用文件。

运行下列文件创建基本应用文件:

1
2
3
touch ~/LargeApp/run.py
touch ~/LargeApp/config.py
touch ~/LargeApp/app/__init__.py

我们当前的结构:

1
2
3
4
5
6
7
8
~/LargeApp
|-- run.py
|-- config.py
|__ /env # Virtual Environment
|__ /app # Our Application Module
|-- __init__.py
|__ /templates
|__ /static

安装 Flask 和相关依赖库

目录什么都创建好之后,我们开始使用 pip 下载安装我们的Flask

使用下述命令将 Flask 安装到虚拟环境当中:

1
2
3
4
cd ~/LargeApp
env/bin/pip install flask
env/bin/pip install flask-sqlalchemy
env/bin/pip install flask-wtf

使用 Modules 和 Blueprints(组件)

基础模块

在走到这一步的时候,我们已经将我们的应用结构创建好了并且也将所有的依赖下载完成了。

我们的目标是模块化可逻辑分组的所有相关模块。

举个简单的例子比如认证系统。将所有视图,控制器,模型和帮助器置于一个位置,以允许可重用性的方式进行设置,这使得这种结构化成为维护应用程序同时提高生产力的好方法。

目标案例模块(组件)结构(在/app里面):

1
2
3
4
5
6
7
8
9
# Our module example here is called *mod_auth*
# You can name them as you like as long as conventions are followed

/mod_auth
|-- __init__.py
|-- controllers.py
|-- models.py
|-- ..
|-- .

模块模板

为了使得达到最大的模块化,我们将遵循上述规定创建 “templates” 文件夹,同时并保护一个与模块相同或相似的相关名称的新文件夹,以包含其模板文件。

目标样例模板目录结构(在 LargeApp 中):

1
2
3
4
5
6
7
8
/templates
|-- 404.html
|__ /auth
|-- signin.html
|-- signup.html
|-- forgot.html
|-- ..
|-- .

创建应用

在本节中,我们将继续前面的步骤,并从我们的应用程序的实际编码开始,然后再转到创建第一个模块化组件(使用蓝图):mod_auth,其用来处理所有与身份验证相关的过程。

使用 nano 编辑 “run.py”

1
nano ~/LargeApp/run.py

输入如下内容:

1
2
3
# Run a test server.
from app import app
app.run(host='0.0.0.0', port=8080, debug=True)

保存退出。

使用 nano 编辑 “config,py”

1
nano ~/LargeApp/config.py

输入如下内容:

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
# Statement for enabling the development environment
DEBUG = True

# Define the application directory
import os
BASE_DIR = os.path.abspath(os.path.dirname(__file__))

# Define the database - we are working with
# SQLite for this example
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, 'app.db')
DATABASE_CONNECT_OPTIONS = {}

# Application threads. A common general assumption is
# using 2 per available processor cores - to handle
# incoming requests using one and performing background
# operations using the other.
THREADS_PER_PAGE = 2

# Enable protection agains *Cross-site Request Forgery (CSRF)*
CSRF_ENABLED = True

# Use a secure, unique and absolutely secret key for
# signing the data.
CSRF_SESSION_KEY = "secret"

# Secret key for signing cookies
SECRET_KEY = "secret"

保存退出。

创建模块/组件

本节是定义本文核心的第一个主要步骤。 在这里,我们将看到如何使用Flask的蓝图来创建一个模块(即组件)。

第一步 结构化模块

正如我们已经开始做的,让我们创建我们的第一个模块(mod_auth)目录和文件来开始工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Create the module directory inside the *app* module
mkdir ~/LargeApp/app/mod_auth

# Create where module's templates will reside
mkdir ~/LargeApp/app/templates/auth

# Create __init__.py to set the directory as a Python module
touch ~/LargeApp/app/mod_auth/__init__.py

# Create module's controllers and models etc.
touch ~/LargeApp/app/mod_auth/controllers.py
touch ~/LargeApp/app/mod_auth/models.py
touch ~/LargeApp/app/mod_auth/forms.py

# Create module's templates
touch ~/LargeApp/app/templates/auth/signin.html

# Create a HTTP 404 Error page
touch ~/LargeApp/app/templates/404.html

在上述操作过程之后,我们的目录结构类似这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
~/LargeApp
|-- run.py
|-- config.py
|__ /env # Virtual Environment
|__ /app # Our Application Module
|-- __init__.py
|-- /mod_auth # Our first module, mod_auth
|-- __init__.py
|-- controllers.py
|-- models.py
|-- forms.py
|__ /templates
|-- 404.html
|__ /auth
|-- signin.html
|__ /static

第二步 定义模块数据模型

1
nano ~/LargeApp/app/mod_auth/models.py

输入下述内容

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
# Import the database object (db) from the main application module
# We will define this inside /app/__init__.py in the next sections.
from app import db

# Define a base model for other database tables to inherit
class Base(db.Model):

__abstract__ = True

id = db.Column(db.Integer, primary_key=True)
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())

# Define a User model
class User(Base):

__tablename__ = 'auth_user'

# User Name
name = db.Column(db.String(128), nullable=False)

# Identification Data: email & password
email = db.Column(db.String(128), nullable=False,
unique=True)
password = db.Column(db.String(192), nullable=False)

# Authorisation Data: role & status
role = db.Column(db.SmallInteger, nullable=False)
status = db.Column(db.SmallInteger, nullable=False)

# New instance instantiation procedure
def __init__(self, name, email, password):

self.name = name
self.email = email
self.password = password

def __repr__(self):
return '<User %r>' % (self.name)

保存并退出

第三步 定义模块表单

1
nano ~/LargeApp/app/mod_auth/forms.py

输入下述内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Import Form and RecaptchaField (optional)
from flask.ext.wtf import Form # , RecaptchaField

# Import Form elements such as TextField and BooleanField (optional)
from wtforms import TextField, PasswordField # BooleanField

# Import Form validators
from wtforms.validators import Required, Email, EqualTo


# Define the login form (WTForms)

class LoginForm(Form):
email = TextField('Email Address', [Email(),
Required(message='Forgot your email address?')])
password = PasswordField('Password', [
Required(message='Must provide a password. ;-)')])

保存并退出

第四步 定义应用控制器(视图)

1
nano ~/LargeApp/app/mod_auth/controllers.py

输入下述内容

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
# Import flask dependencies
from flask import Blueprint, request, render_template, \
flash, g, session, redirect, url_for

# Import password / encryption helper tools
from werkzeug import check_password_hash, generate_password_hash

# Import the database object from the main app module
from app import db

# Import module forms
from app.mod_auth.forms import LoginForm

# Import module models (i.e. User)
from app.mod_auth.models import User

# Define the blueprint: 'auth', set its url prefix: app.url/auth
mod_auth = Blueprint('auth', __name__, url_prefix='/auth')

# Set the route and accepted methods
@mod_auth.route('/signin/', methods=['GET', 'POST'])
def signin():

# If sign in form is submitted
form = LoginForm(request.form)

# Verify the sign in form
if form.validate_on_submit():

user = User.query.filter_by(email=form.email.data).first()

if user and check_password_hash(user.password, form.password.data):

session['user_id'] = user.id

flash('Welcome %s' % user.name)

return redirect(url_for('auth.home'))

flash('Wrong email or password', 'error-message')

return render_template("auth/signin.html", form=form)

保存并退出

第五步 在应用中创建 “app/init.py”

1
nano ~/LargeApp/app/__init__.py

输入以下内容:

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
# Import flask and template operators
from flask import Flask, render_template

# Import SQLAlchemy
from flask.ext.sqlalchemy import SQLAlchemy

# Define the WSGI application object
app = Flask(__name__)

# Configurations
app.config.from_object('config')

# Define the database object which is imported
# by modules and controllers
db = SQLAlchemy(app)

# Sample HTTP error handling
@app.errorhandler(404)
def not_found(error):
return render_template('404.html'), 404

# Import a module / component using its blueprint handler variable (mod_auth)
from app.mod_auth.controllers import mod_auth as auth_module

# Register blueprint(s)
app.register_blueprint(auth_module)
# app.register_blueprint(xyz_module)
# ..

# Build the database:
# This will create the database file using SQLAlchemy
db.create_all()

保存并退出

第六步 创建模板

1
nano ~/LargeApp/app/templates/auth/signin.html

输入以下内容:

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
47
48
49
50
{% macro render_field(field, placeholder=None) %}
{% if field.errors %}
<div>
{% elif field.flags.error %}
<div>
{% else %}
<div>
{% endif %}
{% set css_class = 'form-control ' + kwargs.pop('class', '') %}
{{ field(class=css_class, placeholder=placeholder, **kwargs) }}
</div>
{% endmacro %}

<div>
<div>
<legend>Sign in</legend>
{% with errors = get_flashed_messages(category_filter=["error"]) %}
{% if errors %}
<div>
{% for error in errors %}
{{ error }}<br>
{% endfor %}
</div>
{% endif %}
{% endwith %}

{% if form.errors %}
<div>
{% for field, error in form.errors.items() %}
{% for e in error %}
{{ e }}<br>
{% endfor %}
{% endfor %}
</div>
{% endif %}
<form method="POST" action="." accept-charset="UTF-8" role="form">
{{ form.csrf_token }}
{{ render_field(form.email, placeholder="Your Email Address",
autofocus="") }}
{{ render_field(form.password, placeholder="Password") }}
<div>
<label>
<input type="checkbox" name="remember" value="1"> Remember Me
</label>
<a role="button" href="">Forgot your password?</a><span class="clearfix"></span>
</div>
<button type="submit" name="submit">Sign in</button>
</form>
</div>
</div>

保存并退出

第七步 从网页浏览你定义的模块

模块创建成功了,接下来就是查看我们定义的模板了

运行 run.py 文件启动开发环境服务器

1
2
cd ~/LargeApp
env/bin/python run.py

上述命令将会初始化一个开发环境服务器,端口为 8080.
通过 URL 去访问我们的模块

1
http://[your droplet's IP]/auth/signin

虽然您无法登录,但您可以通过输入一些示例数据或通过测试其验证器来查看它。

部署过程中出现错误

[emerg] 10255#0: bind() to 0.0.0.0:80 failed (98: Address already in use)

解决办法,使用如下命令关闭占用80端口的程序

1
sudo fuser -k 80/tcp

准备工作

安装相关的软件

使用如下命令行,完成相应的 python-pip python-devngnix 安装

1
2
sudo apt-get update
sudo apt-get install python-pip python-dev nginx

创建 Python的虚拟环境

参考我的另一文章为什么使用virtualenv

安装成功之后,然后创建为我们的flask项目创建一个目录,然后在该目录下创建虚拟环境,如下:

1
2
3
mkdir ~/myproject
cd ~/myproject
virtualenv myprojectenv

关于 virtuelenv 相关使用这里不再讲述

创建flask项目

安装 Flask 和 uWSGI

使用 pip 安装 Falsk 和 uWSGI,这里我们不需要激活虚拟环境

1
pip install uwsgi flask

创建应用

这里作为演示,我们将会在一个文件中创建一个简单的应用.使用下面命令:

1
vim ~/myproject/myproject.py

然后在文件中键入下述代码:

1
2
3
4
5
6
7
8
9
from flask import Flask
application = Flask(__name__)

@application.route("/")
def hello():
return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
application.run(host='0.0.0.0')

保存并退出,然后我们通过命令行运行我们的应用了:

1
python myproject.py

然后通过浏览器访问,终端输出的 IP 地址或者域名(xxxx:5000),然后你可能会看见下图:

创建 WSGI 入口

经过刚才验证,我们已经可以确认项目可以运行,接下来我们将创建一个文件,作为我们应用的入口.通过这个文件我们可以很好的与 uWSGI 进行交互.通过命令行创建 wsgi.py 文件

1
vim ~/myproject/wsgi.py

这个文件十分简单,我们可以简单的将我们的 flask 应用实例引入进来,然后运行它

1
2
3
4
5
# wsgi.py
from myproject import application

if __name__ == "__main__":
application.run()

配置 uWSGI

我们的应用基本完成然后我们应用的入口也成功的创建了,接下来我们就需要开始 uWSGI 的配置了

测试 uWSGI 的服务

我们需要做的第一件事就是测试 uWSGI 能不能运行我们的应用.不需要太麻烦,我们只需要通过下述的命令就可以测试:

1
uwsgi --socket 0.0.0.0:8000 --protocol=http -w wsgi

现在你可以通过浏览器访问你的应用,类似xxxx:8000,如果你看见和刚才一样的页面就说明的测试基本是成功的.如果在此之前你激活了你的虚拟环境,那么接下来记得退出它,接下来的操作都是需要在本地的 Python 环境下.

创建 uWSGI 配置文件

同样是在之前的项目文件下创建 myproject.ini 文件

1
vim ~/myproject/myproject.ini

然后在该文件中键入如下内容:

1
2
3
4
5
6
7
8
9
10
11
[uwsgi]
module = wsgi

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

die-on-term = true

输入完成之后保存并退出.

创建启动脚本

创建启动脚本的目的就是为了在服务运行的时候可以运行我们的 Flask 应用.我们需要在*/etc/init*目录下创建一个 .conf的文件,这里我们就以 myproject.conf为例

1
sudo vim /etc/init/myproject.conf

然后键入如下内容:

1
2
3
4
5
6
7
8
9
10
11
description "uWSGI server instance configured to serve myproject"

start on runlevel [2345]
stop on runlevel [!2345]

setuid user
setgid www-data

env PATH=/home/user/myproject/myprojectenv/bin
chdir /home/user/myproject
exec uwsgi --ini myproject.ini

添加完成后保存并退出,接下来我们就可以通过命令启动该进程了

1
sudo start myproject

配置 Nginx 反向代理

我们的 uWSGI 应用现在已经被启动并运行了,在项目目录下等待着请求.我们还需要通过配置 Nginx 将 web 请求转发到该接口上.

创建 Nginx 配置文件

1
sudo vim /etc/nginx/sites-available/myproject

然后键入如下命令:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name server_domain_or_IP;

location / {
include uwsgi_params;
uwsgi_pass unix:/home/user/myproject/myproject.sock;
}
}

添加完成后保存并退出.为了保证我们的配置能够生效,我们需要将文件链接到 sites-enabled 目录下:

1
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

我们可以通过下述命令测试我们创建的文件语法是否正确:

1
sudo nginx -t

如果没有返回任何错误信息,我们可以重启 Nginx 重新加载我们的配置

1
sudo service nginix restart

现在你可以通过浏览器直接访问你的域名或者IP地址看到我们的应用了.

参考地址:https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-uwsgi-and-nginx-on-ubuntu-14-04

效果预览

在上代码之前首先让我们看一下链家网的加载效果,也就是这次我们的模仿对象,效果如下图:

下面是我们实现的效果:

实现思路

Zoom 改变的时候,根据圆的RadiuZoom,计算出适合当前地图的圆的半径,然后重新绘圆

代码

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Gis展示</title>
<link rel="stylesheet" href="jslib/openlayer3/css/ol.css" type="text/css">
<link rel="stylesheet" href="jslib/jeasyui/themes/default/easyui.css" type="text/css">
<script src="jslib/openlayer3/build/ol-debug.js"></script>
<script src="jslib/jquery.min.js"></script>
<script src="jslib/jeasyui/jquery.easyui.min.js"></script>
</head>
<body>
<div id="mapContainer" class="subcontent">
<div id="map" class="map" style="width: auto;height: auto"></div>
</div>
<script>
var styles = {
'Circle': new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(57,172,106,0.9)',
opacity: '0.05'
}),
text: new ol.style.Text({
font: '14px sans-serif',
text:'Test\n\n Value \n\n Message',
textAlign:'center',
rotateWithView:true,
fill: new ol.style.Fill({
color: '#FFFFFF'
}),
stroke:new ol.style.Stroke({
color:'#FFFFFF',
width: 1
})
})
// radius:100
})
};
var Selectstyles = {
'Circle': new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(228,57,60,0.9)',
opacity: '0.05'
}),
text: new ol.style.Text({
font: '14px sans-serif',
text:'Test\n\n Value \n\n Message',
textAlign:'center',
// rotateWithView:true,
fill: new ol.style.Fill({
color: '#FFFFFF'
}),
stroke:new ol.style.Stroke({
color:'#FFFFFF',
width: 1
})
})
})
};
var styleFunction = function(feature) {
return styles[feature.getGeometry().getType()];
};
var selectStyleFunction = function(feature) {
return Selectstyles[feature.getGeometry().getType()];
}
var vectorSource = new ol.source.Vector({
features: new ol.Feature(new ol.geom.Circle([5e6, 7e6], 1e6))
});
vectorSource.addFeature(new ol.Feature(new ol.geom.Circle([5e6, 7e6], 1e6/Math.pow(2,-1))));
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: styleFunction
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
var view = map.getView();
view.on('change:resolution',function(event){
var zoom = view.getZoom();
var Collection = map.getLayers();
var layers = Collection.getArray();
var vectorSource = layers[1].getSource();
var features = vectorSource.getFeatures();
var len = features.length;
for(var i = 0 ; i< len; i++) {
var Circle = features[i].getGeometry();
console.log(zoom);
Circle.setRadius(1e6/Math.pow(2,zoom-3));
}
});
var selectPointerMove = new ol.interaction.Select({
condition: ol.events.condition.pointerMove
});
map.addInteraction(selectPointerMove);
selectPointerMove.on('select',function(e) {
var ele = e;
var select_len = e.selected.length;
var unselect_len = e.deselected.length;
if (select_len > 0) {
var featureArr = e.selected;
for (var i = 0; i < featureArr.length; i++) {
featureArr[i].setStyle(selectStyleFunction(featureArr[i]));
var Circle = featureArr[i].getGeometry();
}
}
if (unselect_len > 0) {
var featureArr = e.deselected;
for (var i = 0; i < featureArr.length; i++) {
featureArr[i].setStyle(styleFunction(featureArr[i]));
var Circle = featureArr[i].getGeometry();
}
}
})
</script>
</body>
</html>

Package epel-release.noarch 0:7-9 will be installed
–> Finished Dependency Resolution

1
2
3
4
5

### 安装 python-pip

```shell
[root@localhost Desktop]# yum install python-pip

安装完成之后,测试是否安装成功

测试是否安装成功

1
2
[root@localhost Desktop]# pip --version
pip 8.1.2 from /usr/lib/python2.7/site-packages (python 2.7)

安装完成后,清理 cache

1
2
3
4
5
[root@localhost Desktop]# yum clean all
Loaded plugins: fastestmirror, langpacks
Cleaning repos: base epel extras updates
Cleaning up everything
Cleaning up list of fastest mirrors

最近想用 Openlayers3 根据 Zoom 的级别,从而切换不同的图层,查询了相关资料和在同事的帮助下完成了如下的效果:

实现代码如下:

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
47
48
49
50
51
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Gis展示</title>
<link rel="stylesheet" href="jslib/openlayer3/css/ol.css" type="text/css">
<script src="jslib/openlayer3/build/ol.js"></script>
<script src="jslib/jquery.min.js"></script>
<script src="jslib/Gis.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
var country = '第一图层请求地址';
var province = '第二图层请求地址';
var city = '第三图层请求地址';

var layerCount = 2;
var map = Gis().map();
var view = map.getView();
Gis().saveOnlyLayer(map,'China');
map.addLayer(Gis().createGeoLayer(country,'country'));
/*
默认加载全国图层,当处于处于全国区域,加载省份图层,将其隐藏
当处于省份图层加载城市图层,将其隐藏,从而提高价值速度
*/
view.on('change:resolution',function(event){
var zLevel = view.getZoom();
// event
if (zLevel <= '5') {
if (layerCount >= 0 )
{
Gis().addMapLayer(Gis().createGeoLayer(province,'province'),'province');
}
layerCount -= 1;
Gis().viewLayer(map, 'country');
}
else if (zLevel <= '7') {
if (layerCount >= 0 ) {
Gis().addMapLayer(Gis().createGeoLayer(city,'city'),'city');
}
layerCount -= 1;
Gis().viewLayer(map, 'province');
} else{
Gis().viewLayer(map, 'city');
}
console.log(zLevel);
});
</script>
</body>
</html>

关于图层切换提高其加载速度和效率,初始情况下加载第一图层,在 Zoom 属于第一图层的级别中,加载第二图层数据并将其隐藏,同理在 Zoom 属于第二图层的级别中,加载第三图层数据并隐藏,通过 layerCount 变量确保三个图层数据加载请求一次,三个图层加载完成,只有通过 layer 的 setVisible 方法实现图层的显示.其中 Gis.js 文件是自己封装的关于 Openlayers3 的常见操作.

完整代码如下:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
var Gis = function() {
//坐标系
var deproj = ol.proj.get('EPSG:4326');
//中心坐标
var coordinates = [113.653055, 34.800358];
//矢量层数据源
var vectorSource = 'gisData.json';
//默认的style
var image = new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: 'red'
}),
stroke: new ol.style.Stroke({color: 'red', width: 1})
});
var styles = {
'Point': new ol.style.Style({
image: image
}),
'LineString': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
}),
'MultiLineString': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
}),
'MultiPoint': new ol.style.Style({
image: image
}),
'MultiPolygon': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 0, 0.1)'
})
}),
'Polygon': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
lineDash: [4],
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
}),
'GeometryCollection': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'magenta',
width: 2
}),
fill: new ol.style.Fill({
color: 'magenta'
}),
image: new ol.style.Circle({
radius: 10,
fill: null,
stroke: new ol.style.Stroke({
color: 'magenta'
})
})
}),
'Circle': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'red',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255,0,0,0.2)'
})
})
};

var maxvalue = 4;
var minvalue = 0;
var changeStyles = {
'Point': new ol.style.Style({
image: image
}),
'LineString': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
}),
'MultiLineString': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
}),
'MultiPoint': new ol.style.Style({
image: image
}),
'MultiPolygon': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'yellow',
width: 1
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 0, 0.1)'
})
}),
'Polygon': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
lineDash: [4],
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
}),
'GeometryCollection': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'magenta',
width: 2
}),
fill: new ol.style.Fill({
color: 'magenta'
}),
image: new ol.style.Circle({
radius: 10,
fill: null,
stroke: new ol.style.Stroke({
color: 'magenta'
})
})
}),
'Circle': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'red',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255,0,0,0.2)'
})
})
};
function getColorByLevel(level) {
// console.log(level+"int"+parseInt(level));
if(parseInt(level) == 1){
return '#cc0000';
}
if(parseInt(level) == 2){
return '#ff9900';
}
if(parseInt(level) == 4){
return '#8dc43c';
}
if(parseInt(level) == 3){
return '#b6d7a8';
}
return '#ffffff';
}
function getColorByPercentValue(value) {
// console.log(value);
if(parseFloat(value) >= 0.3 && parseFloat(value) <= 1){
return '#cc0000';
}
if(parseFloat(value) >= 0.2 && parseFloat(value) < 0.3){
return '#ff9900';
}
if(parseFloat(value) >= 0.1 && parseFloat(value) < 0.2){
return '#b6d7a8';
}
if(parseFloat(value) >= 0 && parseFloat(value) < 0.1){
return '#8dc43c';
}
return '#ffffff';
}
function getColorByDensityValue(value) {
// console.log(value);
if(value == 'A' || value == '1'){
return '#cc0000';
}
if(value == 'B' || value == '2'){
return '#ff9900';
}
if(value == 'C' || value == '3'){
return '#b6d7a8';
}
if(value == 'D' || value == '4'){
return '#8dc43c';
}
return '#ffffff';
}
//获取宫格的填充样式,根据宫格ID获取宫格对应的指标值,然后进行插值
function getFillStyle(feature) {
var gridvalue = feature.getProperties().value;
var color = getColorByLevel(gridvalue);
// console.log(color);
var styleArray = [new ol.style.Style({
fill: new ol.style.Fill({
color: color
}),
stroke: new ol.style.Stroke({
color: '#000000',
width: 1
})
})];
return styleArray;
}
function getNetFillStyle(feature){
var gridvalue = feature.getProperties().value;
var color = getColorByPercentValue(gridvalue);
// console.log(color);
var styleArray = [new ol.style.Style({
fill: new ol.style.Fill({
color: color
}),
stroke: new ol.style.Stroke({
color: '#000000',
width: 1
})
})];
return styleArray;
}
function getDensityFillStyle(feature){
var gridvalue = feature.getProperties().value;
console.log(gridvalue);
var color = getColorByDensityValue(gridvalue);
var styleArray = [new ol.style.Style({
fill: new ol.style.Fill({
color: color
}),
stroke: new ol.style.Stroke({
color: '#000000',
width: 1
})
})];
return styleArray;
}
/**
* 设置默认投影系
*
* @param proj 新的投影系
*/
function setDeproj(proj) {
deproj = ol.proj.get(proj);
}
/**
* 设置地图中心点坐标
*
* @param coord[Array] 中心点坐标
*/
function setCoordinates(coord) {
coordinates = [coord[0],coord[1]];
}
/**
* 设置矢量图层数据源
*
* @param source[string] 矢量图层来源
*/
function setVectorLayerSource(source) {
vectorSource = source;
}
/**
* 设置显示的样式
*
* @param style 样式
*/
function setStyle(style) {
styles = style;
}
/**
* 根据不同feature按照不同样式显示
*
* @param feature 坐标数据
*/
function styleFunction(feature) {
// console.log(feature.getGeometry().getType());
return styles[feature.getGeometry().getType()];
}
function cstyleFunction(feature) {
//console.log(feature.getGeometry().getType());
return styles[feature.getGeometry().getType()];
}
/**
* 获得矢量图层
*
* @param title 根据title创建指定图层
*
* @return Object 矢量层对象
*/
function getGeoLayer(title) {
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: new ol.source.Vector({
url: vectorSource,
format: new ol.format.GeoJSON()
}),
style: styleFunction
});
return GeoJsonLayer;
}

function getKmlLayer(title) {
var KMLLayer = new ol.layer.Vector({
title: title,
source: new ol.source.Vector({
url: vectorSource,
format: new ol.format.KML()
}),
style: styleFunction
});
return KMLLayer;
}

function createGeoLayer(source, title) {
if (typeof(source) == 'object') {
console.log("readFeatures");
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(source)
});
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: vectorSource,
updateWhileInteracting: true,
style: styleFunction
});
} else {
console.log("sourceUrl");
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: new ol.source.Vector({
url: source,
format: new ol.format.GeoJSON()
}),
updateWhileInteracting: true,
style: styleFunction
});
}
return GeoJsonLayer;
}

function createKMLLayer(sourceUrl, title) {
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: new ol.source.Vector({
url: sourceUrl,
format: new ol.format.KML({
extractStyles: false
})
}),
// updateWhileInteracting: true,
style: cstyleFunction
});
console.log("createKMLLayer");
return GeoJsonLayer;
}
function createGridGeoLayer(source, title) {
if (typeof(source) == 'object') {
console.log("readFeatures");
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(source)
});
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: vectorSource,
updateWhileInteracting: true,
style: function(feature, resolution) {
return getFillStyle(feature);
}
});
} else {
console.log("sourceUrl");
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: new ol.source.Vector({
url: source,
format: new ol.format.GeoJSON()
}),
updateWhileInteracting: true,
style: function(feature, resolution) {
return getFillStyle(feature);
}
});
}
return GeoJsonLayer;
}
function createPercentGeoLayer(source, title){
if (typeof(source) == 'object') {
console.log("readFeatures");
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(source)
});
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: vectorSource,
updateWhileInteracting: true,
style: function(feature, resolution) {
return getNetFillStyle(feature);
}
});
} else {
console.log("sourceUrl");
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: new ol.source.Vector({
url: source,
format: new ol.format.GeoJSON()
}),
updateWhileInteracting: true,
style: function(feature, resolution) {
return getNetFillStyle(feature);
}
});
}
return GeoJsonLayer;
}
function createDensityGeoLayer(source, title){
if (typeof(source) == 'object') {
console.log("readFeatures");
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(source)
});
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: vectorSource,
updateWhileInteracting: true,
style: function(feature, resolution) {
return getDensityFillStyle(feature);
}
});
} else {
console.log("sourceUrl");
var GeoJsonLayer = new ol.layer.Vector({
title: title,
source: new ol.source.Vector({
url: source,
format: new ol.format.GeoJSON()
}),
updateWhileInteracting: true,
style: function(feature, resolution) {
return getDensityFillStyle(feature);
}
});
}
return GeoJsonLayer;
}
/**
* 根据文件名称获取文件后缀名
*
* @param filename 文件名
*
* @return string 后缀名
*/
function getFileExtension(filenaem) {
var result = /\.[^\.]+/.exec(filenaem);
return result;
}
/**
* 获取地图信息
*
* @return Object map对象
*/
function getMap() {
if (arguments.length === 1) {
setVectorLayerSource(arguments[0]);
}
if (getFileExtension(arguments[0]) == 'json') {
var vectorLayer = getGeoLayer();
} else if (getFileExtension(arguments[0]) == 'kml') {
var vectorLayer = getKmlLayer();
} else {
var vectorLayer = new ol.layer.Vector({
title: 'station Layer',
source: new ol.source.Vector({
format: new ol.format.KML()
}),
style: styleFunction
});
}
var map = new ol.Map({
layers: [
new ol.layer.Tile({
title: 'China',
source: new ol.source.OSM()
}),
vectorLayer
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
projection: deproj,
center: coordinates,
zoom: 4,
maxZoom: 16,
minZoom: 4
})
});
return map;
}
/**
* 保留指定图层,移除其他图层
*
* @param map 地图对象
*
* @param tilte 指定图层的title
*/
var removeLayerExceptOne = function(map, title) {
var layers = map.getLayerGroup().getLayers()['a'];
for (var i = 0; i < layers.length; i++) {
console.log(layers[i]['U']['title']);
if (layers[i]['U']['title'] != 'China' && layers[i]['U']['title'] != title) {
console.log("remove" + layers[i]['U']['title']);
map.removeLayer(layers[i]);
}
}
}
/**
* 显示指定图层,隐藏其他图层
*
* @param {object} map
* @param {string} title
*/
var hiddenLayerExceptOne = function(map, title) {
var layers = map.getLayerGroup().getLayers()['a'];
for (var i = 0; i < layers.length; i++) {
console.log(layers[i]['U']['title']);
if (layers[i]['U']['title'] != 'China' && layers[i]['U']['title'] != title) {
console.log("hidden" + layers[i]['U']['title']);
var layer = layers[i];
layer.setVisible(false);
} else{
var layer = layers[i];
layer.setVisible(true);
}
}
}
/**
* 添加图层,并区分是否存在当前 title 值的图层
*
* @param {object} layer
* @param {string} title
*/
var addMapLayer = function(layer, title) {
if (!isLayerExist(title)) {
layer.setVisible(false);
map.addLayer(layer);
}
console.log('当前图层 title 已存在:'+ title);
}
/**
* 判断是否存在当前 title 的图层
* @param {string} title
*/
var isLayerExist = function (title) {
var layers = map.getLayerGroup().getLayers();
var layers_len = layers.length;
for (var i = 0; i < layers_len; i++) {
if (layers['a'][i]['U']['title'] == title) {
return true;
}
}
return false;
}

var trim = function(str) {
return str.replace(/(^\s*)|(\s*$)/g, "")
}
//返回的gisApi
var gisAPI = {
map: getMap,
setProj: setDeproj,
setCenter: setCoordinates,
setSource: setVectorLayerSource,
createGeoLayer: createGeoLayer,
createKMLLayer: createKMLLayer,
saveOnlyLayer: removeLayerExceptOne,
createGridGeoLayer: createGridGeoLayer,
createPercentGeoLayer: createPercentGeoLayer,
createDensityGeoLayer: createDensityGeoLayer,
viewLayer:hiddenLayerExceptOne,
addMapLayer: addMapLayer
};
return gisAPI;
}

本文所有均在 Ubuntu 下完成

selenium 安装

使用 pip3 安装 selenium

1
sudo pip3 install selenium

关于 pip3 的安装,使用下列命令

1
sudo apt-get install python3-pip

selenuim 安装成功之后就是使用

selenuim 使用

使用下面的简单例子测试一下

1
2
3
4
5
6
7
8
9
10
11
12
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

driver = webdriver.Firefox()
driver.get("http://www.python.org")
assert "Python" in driver.title
elem = driver.find_element_by_name("q")
elem.clear()
elem.send_keys("python")
elem.send_keys(Keys.RETURN)
assert "No results found" not in driver.page_source
driver.close()

使用中错误

selenium.common.exceptions.WebDriverException: Message: ‘geckodriver’ executable needs to be in PATH.

解决方法:

  1. 下载 geckdriver
  2. 将其解压并使用 chomod +x 使其可执行,然后拷贝到 /usr/local/bin 目录下

selenium.common.exceptions.WebDriverException: Message: Unsupported Marionette protocol version 2, required 3

解决方法

将你的 Firfox 浏览器更新,确保你的版本要 >= v47,使用下列命令进行更新

1
2
sudo apt-get update
sudo apt-get install firefox

然后重启 firefox

运行

1
python3 xxx.py

然后你会发现 firefox 被打开,跳转到 “http://www.python.org",然后搜索框填入 “python”,然后跳转到搜索页面,最后的截图如下:

:|
| Oracle base | /u01/app/oracle |
| Software location | /u01/app/oracle/product/12.1.0/dbhome_1 |
| Database file location | /u02 |
| Global database name | orcl |

另外请设置合适的 Database edition(数据库版本)及 Character set(字符集)。请为数据库的管理订立一个安全的口令,最后请取消勾选 Create as Container database 项目

口令:密码由数字和字母组成,必须包含一个大写字母和一个小写字母
字符集:Unicode(AL32UTF8)

第五步 —— 创建库存

安装缺省的设置 /u01/app/oraInventory 并按 Next。

第六步 —— 检查先决条件

安装程序会自动检查所有必须的操作系统组件及内核设置。

第七步 —— 摘要

这是配置安装的最后机会,请按 Install。

第八步 —— 安装

当询问窗口出现时,请登录成为 root 并执行两个脚本:

1
2
3
4
5
6
[root@bogon lt94]# /u01/app/oraInventory/orainstRoot.sh
Changing permissions of /u01/app/oraInventory.
Adding read,write permissions for group.
Removing read,write,execute permissions for world.
Changing groupname of /u01/app/oraInventory to oinstall.
The execution of the script is complete.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@bogon lt94]# /u01/app/oracle/product/12.1.0/dbhome_2/root.sh
Performing root user operation.
The following environment variables are set as:
ORACLE_OWNER= oracle
ORACLE_HOME= /u01/app/oracle/product/12.1.0/dbhome_2
Enter the full pathname of the local bin directory: [/usr/local/bin]: <PRESS ENTER>
Copying dbhome to /usr/local/bin ...
Copying oraenv to /usr/local/bin ...
Copying coraenv to /usr/local/bin ...
Creating /etc/oratab file...
Entries will be added to the /etc/oratab file as needed by
Database Configuration Assistant when a database is created
Finished running generic part of root script.
Now product-specific root actions will be performed.
You can follow the installation in a separated window.

这两个脚本都必须以 root 的身份来执行,然后出现一个进度条,等待安装完成

第九步 —— 顺利完成安装

最后一个画面将会通知你安装已经完成并显示 Oracle 企业级管理员的 URL。

https://localhost:5500/em

请按 OK 来关闭安装程序。

安装完成后的任务

防火墙

请登录成为 root 并检查已引导的本地

1
2
3
[root@bogon lt94]# firewall-cmd --get-active-zones
public
interfaces: eno16777736

打开相关的端口

1
2
[root@bogon lt94]# firewall-cmd --zone=public --add-port=1521/tcp --add-port=5500/tcp --add-port=5520/tcp --add-port=3938/tcp --permanent
success
1
2
[root@bogon lt94]# firewall-cmd --reload
success
1
2
[root@bogon lt94]# firewall-cmd --list-ports
1521/tcp 3938/tcp 5500/tcp 5520/tcp

Oracle 工作环境

请登录为 oracle 用户并在 /home/oracle/.bash_profile 内加入下列数值

1
2
[root@bogon lt94]# su oracle
[oracle@bogon lt94]$ vim /home/oracle/.bash_profile
1
2
3
4
5
6
7
TMPDIR=$TMP; export TMPDIR
ORACLE_BASE=/u01/app/oracle; export ORACLE_BASE
ORACLE_HOME=$ORACLE_BASE/product/12.1.0/dbhome_2; export ORACLE_HOME
ORACLE_SID=orcl; export ORACLE_SID
PATH=$ORACLE_HOME/bin:$PATH; export PATH
LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib:/usr/lib64; export LD_LIBRARY_PATH
CLASSPATH=$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib; export CLASSPATH

重新加载 bash_profile 使其生效

1
[oracle@bogon lt94]$ . /home/oracle/.bash_profile 

登录数据库

最后请登录数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[oracle@bogon lt94]$ sqlplus system@orcl

SQL*Plus: Release 12.1.0.2.0 Production on Wed Dec 21 18:28:20 2016

Copyright (c) 1982, 2014, Oracle. All rights reserved.

Enter password:
Last Successful login time: Wed Dec 21 2016 18:25:24 -08:00

Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options

SQL>

请利用 Oracle 企业级管理员来管理数据库:
https://<主机名称>:5500/em

This will configure on-boot properties of Oracle Database 11g Express
Edition. The following questions will determine whether the database should
be starting upon system boot, the ports it will use, and the passwords that
will be used for database accounts. Press to accept the defaults.
Ctrl-C will abort.

Specify the HTTP port that will be used for Oracle Application Express [8080]:

Specify a port that will be used for the database listener [1521]:

Specify a password to be used for database accounts. Note that the same
password will be used for SYS and SYSTEM. Oracle recommends the use of
different passwords for each database account. This can be done after
initial configuration:
Password can’t be null. Enter password:
Confirm the password:

Do you want Oracle Database 11g Express Edition to be started on boot (y/n) [y]:y

Starting Oracle Net Listener…Done
Configuring database…Done
Starting Oracle Database 11g Express Edition instance…Done
Installation completed successfully.

1
2
3
4
5
6
7
8
9

安装成功后将会创建一个 /u01 的文件

# 环境配置

安装完成之后为了设置 Oracle 运行所需的环境变量,执行 */u01/app/oracle/product/11.2.0/xe/bin* 下的 *oracle_env.sh* 的脚本

```shell
[root@bogon local]# cd /u01/app/oracle/product/11.2.0/xe/bin/

针对当前用户设置环境变量,运行 . ./oracle_env.sh 命令

1
[root@bogon bin]# . ./oracle_env.sh

如果想让其他用户拥有同样的权限,可以将下述命令添加到你需要的用户的 .bashrc 或者 .bash_profile 文件当中去

1
. /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh  

现在我们可以尝试通过命令行访问 SQLPlus* 了

1
2
3
4
5
6
7
8
9
[root@bogon bin]# sqlplus /nolog

SQL*Plus: Release 11.2.0.2.0 Production on 星期日 12月 18 18:51:42 2016

Copyright (c) 1982, 2011, Oracle. All rights reserved.

SQL> connect sys/Password as sysdba
Connected.
SQL>

允许远程访问 Oracle 11g XE GUI

为了允许远程访问 Oracle 11g XE GUI, 在 SQL*Plus 执行下列语句

1
2
SQL>  EXEC DBMS_XDB.SETLISTENERLOCALACCESS(FALSE); 
PL/SQL 过程已成功完成。

现在你可以访问 Oracle 11g XE GUI 通过该网址 http://localhost:8080/apex/f?p=4950:1
将 locahost 替换为你的ip地址或者域名同样也可以访问,登录使用的密码是配置 Oracle 中设置的密码