要: 在WebGIS的浏览器端存在绘图效率差、不能直接支持矢量绘图等问题。为提高客户端的交互性和实时性,在解决浏览器不支持矢量绘图和渲染速度慢、存储空间小、传输速率慢这些问题的基础上,结合HTML5中的Canvas、WebSocket、WebStorage技术,构建了WebGIS实时客户端,有效地改善了客户端存在的问题,改善用户的体验。
0 引言
WebGIS是GIS技术与Internet的结合体,是对网络GIS的发展,它将GIS从一种使用计算机的处理地理信息的系统工具,变为一种网络共享资源,提高了地理信息的使用效率,使地理信息的价值得以充分体现[1]。WebGIS的实现手段主要是基于Web技术的多级B/S体系结构,即浏览器、GIS服务器、数据库等。B/S模式的构架简化了客户端系统的部署,降低了用户使用难度,同时也极大提高了系统的可维护性[2]。但是这种被动的工作模式无法满足用户实时性的需求。对于WebGIS的架构研究,以前主要是对服务器端技术的改进,如使用CGI、Server API等服务端技术,但是这些技术对于服务器及网络的要求较高。随着客户端技术的发展,Java Applet技术、Plug-in技术、Ajax技术的出现扩展了客户端的功能,但是用户需要预先安装这些插件,安装插件带来了一定的安全隐患和不方便性。对比这两种形式,使用HTML5的新技术实现的功能并不需要插件支持,且其本地存储功能和高效率的双向通信功能,还有动态快速绘图功能的提出,使实时客户端的实现成为可能。
1 实时客户端的WebGIS系统结构
HTML5是W3C推出的新标准,其强大功能已经被大部分的浏览器支持,同时,相对于以前的版本,HTML5的改变不仅仅是使用更方便。第一,它新增了更具有语义化的标签和使用更智能化的表单;第二,HTML5还新增了许多JavaScript API,如实时绘图(Canvas和SVG)、离线存储(WebStorage)、实时通信(WebSocket)API等,这些API为实现实时客户端提供了可能。用HTML5来实现WebGIS有地图无限缩放而图不失真,地图的显示渲染效果逼真的优势;且支持JavaScript脚本实现地图的漫游、缩放、查询等功能,这些功能都无需插件支持并可在不同平台与设备上运行[3]。根据这些特性,本文在传统架构的基础上,构建了图1所示的WebGIS实时客户端。
在客户端这些模块中使用HTML5和JavaScript技术,实现数据地图的绘制、本地存储、数据传输等功能,将部分简单的功能交给客户端完成,增加了WebGIS客户端的交互能力,减少了服务器的传输压力,也增加了数据传输的效率,实现了客户端的实时性。
2 WebGIS客户端的具体设计
2.1 数据实时更新与传输设计
空间数据格式有栅格数据和矢量数据。栅格数据交互性能差,可用来展示空间数据,矢量数据交互性较好,可用来表示空间对象,但是客户端不能直接支持矢量数据的二进制格式传输,所以数据在传输中就要进行格式编码,目前使用的比较多的数据传输格式是GeoJSON(基于JSON数据格式的地理要素表示格式)。JSON(JavaScript Object Notation)是轻量级数据交换格式,适合于服务器与浏览器(通过JavaScript)交互[4],在浏览器端JSON能够简单快速地解析成可以被客户端操作的JavaScript地理信息,不需要使用专门的文本解析API进行操作。空间数据传输大多使用Ajax的异步传输方式,但Ajax轮询方式的时延较长,传输的实时性不高,因此将Ajax用于地图数据的推送。实时性方面还是需要HTML5的WebSocket API实现。WebSocket使浏览器具有客户机/服务器(C/S)模式下应用程序的实时通信能力[5]。WebSocket的优势有:(1)为浏览器和服务器之间建立的更高效的双向通信提供支持。其连接本质为TCP连接,因此浏览器通过JavaScript向服务器发出建立WebSocket连接的请求,onopen事件接收消息,连接建立以后,客户端和服务器端就可通过TCP连接直接交换数据。(2)WebSocket有更为轻量级的Header,除了首次建立链接时需要发送头部与普通Web链接类似的数据之外,连接建立后,相互沟通的Header就会很简洁,大大减少了冗余的数据传输[6];通过新建WebSocket实例建立握手并完成与服务器的连接,建立好连接之后数据就可以以全双工模式在客户端与服务器之间进行双向传输,并一直保持连接,直到用户主动关闭[3]。图2是传统传输方式与WebSocket传输方式数据量的对比,从结果可以看出,当传输数据量增大时,WebSocket的效率更高。
需要在服务器端安装开源的WebSocket支持数据库,如Node.js、LibWebSockets、PHP WebSocket Server等,以调用接口使用。具体传输过程是:GIS服务器从空间数据库获取数据,将空间数据解析成JSON的格式,然后发送到已经与WebSocket服务器连接的客户端,客户端WebSocket实例的onmessage事件接收数据并使用JSON.parse函数将JSON字符串解析成JavaScript对象,再根据对象的内容在WebGIS的地图中解析显示。WebSocket API一个显著特点是,连接建立后,服务器可主动推送消息。支持WebSocket协议的服务端接受请求和处理WebSocket任务,在客户端可用WebStorage存储模块缓存传输过来的数据,WebSocket的中间传输机制就实现了数据实时更新。
2.2 客户端空间数据缓存设计
客户端缓存功能主要是由WebStorage API来实现,WebStorage API是以Key-Value形式来进行数据的持久性存储。传统客户端一般是使用Cookie存储数据,但是它效率低,存储空间小,不能满足大量矢量数据的存储。WebStorage提供的两种存储方式可以改善这个状况,一种是SessionStorage,它是一种会话级别的存储方式,存储的数据只在当前页面有效,当页面关闭时,数据也会随之删除,据此SessionStorage可以用在矢量图绘制的缓存方面,即让SessionStorage自动缓存解析好的JSON格式的矢量层数据,当需要数据时直接从SessionStorage中提取JSON数据,这比从空间数据库中获取再解析效率明显更高,数据显示模块可以直接从中获取数据进行绘制。另一种方式是LocalStorage,它是一个跨多窗口且数据永久存储的方式,只有使用清除函数clear、removeItem或手动删除数据时数据才会从浏览器中清除,同时它的数据可以在同源的窗口或标签共享使用。因此它可以用来永久存储空间分析后的数据或服务器发送的数据和用户数据,下次用户打开浏览器可直接使用这些数据。图3是具体矢量数据本地存储过程。将数据缓存到本地,不用与服务器发生交互,客户端存储的数据可以由JavaScript访问。
WebStorage存储技术和WebSocket技术可结合使用并与JavaScript一起实现数据的实时更新,减轻服务器压力。用户发送数据请求后,WebSocket建立连接,将得到的数据解析后存储在WebStorage预先定义好的空间中,等待用户提取。对于常须更新地图数据的操作采用这种预先存储的方式,可以加快数据的渲染效率。为了提高图3的实时更新效果,可以设定异步存储机制,过程如下:(1)创建并初始化WebStorage对象,设置缓存区大小(缓存区分段);(2)用户发出消息,触发onopen事件;(3)为WebSocket对象的信息添加侦听器;(4)服务器调用received函数处理事件;(5)客户端接收服务器发送的消息,触发缓存判断函数,判断缓存大小,如果超出,设置缓存片段的编号,对象异步sends数据请求服务器处理;(6)服务器将片段编号入队(服务端设置),直到传输完成,清除编号。这种传输方式将进一步减少网络流量,减少服务器的负载,提升传输效率。
2.3 数据显示设计
HTML5现提供了Canvas API,它可以解决传统开发浏览器支持度差、跨平台性不强和绘制矢量图形需要插件的问题,通过JavaScript在浏览器不依赖任何插件的情况下高效地绘制动态图形[7],避免了安装插件带来安全风险。
2.3.1 绘制方法
Canvas API使用比较简单,像其他HTML标签一样,只需在HTML5的页面中添加一个<canvas>元素即可。Canvas绘图首先要获得上下文(context),因为Canvas自己本身并没有绘图能力,所以所有的绘图工作要由JavaScript完成。它的工作过程为:先定义Canvas元素的id,然后通过getElementById函数找到自己前面创建好的Canvas元素,最后用JavaScript调用绘图API接口得到上下文绘图环境后就可在画布上绘制任何的图形了。但Canvas只支持一种基本图形——矩形,想使用Canvas绘制其他的图形,就要使用其提供的路径(Path)绘制函数[8],使用beginPath开始绘制,使用moveTo、lineTo绘制直线,绘制完毕后调用fill、stroke进行填充或者设置边框,最后调用closePath结束图形绘制。Canvas在矢量数据方面是像素级管理,Canvas的路径操作能充分满足矢量数据中的点要素、线要素、面要素以及符号的表达要求。
2.3.2 数据显示过程
用Canvas与JavaScript技术实现地图实时绘制。地理实体对象可以根据OGC的简单要素规范和GIS界的研究将其分为:点状实体、线状实体、面状实体、标注实体和栅格实体[3]。Canvas可直接使用这些实体,对于栅格实体,可以用JavaScript对象表示,通过API drawImage直接在HTML5 Canvas中绘制。矢量实体的地理要素从GeoJSON文本转化为JavaScript对象,根据该对象的信息,调用坐标转换对象,将坐标转换成Canvas元素里的屏幕坐标来对应显示要素。实现实时绘图的流程为:(1)服务器从数据库获取数据;(2)数据进行JSON格式解析;(3)浏览器加载地图,调用Canvas画布及函数实现绘图和地图的显示;(4)地图操作由自定义脚本实现地图渲染。Canvas在客户端展现地理数据的灵活性与互操作性要优于传统的切片技术,并且在地图加载速度方面有明显的优势[8],因此将Canvas和WebStorage结合起来,可以提高实时性能,将要显示或者要存储的数据先经过缓存,再进行显示或存储,这种机制效率明显更高。图4说明了从数据推送到显示的流程。
3 客户端整体过程
Canvas实现实时绘图和快速渲染,WebSocket API在通信方面提供了支持。WebStorage提供了客户端的本地存储技术,以地图放大操作为例,用户提交请求,数据获取函数可以从SessionStorage中获取数据,然后将缓存的数据转换成JavaScript对象,最后通过Canvas直接添加在地图中,同时,缓存中的数据不断更新。WebSocket的双向通信机制和WebStorage的异步存储机制加速了数据交换效率,空间数据库也通过WebGIS服务器,在实时通信的基础上,将数据以JSON文本格式传输到客户端,存入SessionStorage中,窗口关闭后,所有数据存入LocalStorage,供用户下次直接使用。这种方式的效率很明显会比从服务器获取数据的方式高,实时性要好,如需要执行其他的交互操作,也可以基于这种方式实现。
4 结论
随着Web技术的发展,客户端的交互性和实时性要求不断提升,据此,本文将HTML5的新特性与JavaScript技术结合,在WebSocket双向通信机制和WebStorage缓存机制实现异步存储提供的数据支撑,Canvas能实现动态绘图和快速渲染的基础上,提出了一种实时性更强的WebGIS客户端,为建立实时的WebGIS客户端提供了一种方案。HTML5中的新技术虽然强大,还是有需要加强的地方,WebStorage让数据能够缓存在本地,但是由于是存储在本地,数据容易泄漏,安全性还有欠缺。未来WebGIS的发展会随着数据量的增大实现云平台化,资源更庞大,数据更易共享,用户使用起来也会更方便。
参考文献
[1] 孟令奎,史文中,张鹏林,等.网络地理信息系统原理与技术[M].北京:科学出版社,2010.
[2] 李博霏,李欣,李艳明.基于浏览器的地理信息服务客户端技术研究[J].计算机工程与设计,2011(9):3031-3035.
[3] 龙云.基于HTML5的WebGIS研究[D].赣州:江西理工大学,2013.
[4] CROCKFORD D. The application/json media type for JavaScript Object Notation(JSON)[EB/OL]. [2006-07]. http://tools.ietf.org/html/rfc4627.
[5] 徐卓揆.基于HTML5、Ajax和Web Service的WebGIS研究[J].测绘科学,2012,37(1):145-147.
[6] 魏进锋,孙春华.应用HTML5的WebSocket实现BiDirection数据交换[EB/OL].[2012-12-28](2015-02-25).http://www.ibm.com/developerworks/cn/web/1112_weijf_websocket/.
[7] 王晓.基于HTML5的矢量地图发布关键技术研究[D].南京:南京师范大学,2011.
[8] 梁春雨,李新通.使用HTML5 Canvas构建基于GeoJSON的轻量级WebGIS[J].计算机科学与应用,2012(2):189-196.
击查看智能机器人动图
javascript课题:Web前端开发人工智能机器人
知识点: ajax概念解析,ajax封装,如何调用数据库
函数封装,javascript正则匹配,元素创建,企业开发标准
2017就业形势分析,面试技巧等
PS:现在人工智能很火,包括前端时间,腾讯的群里面都有了官方的机器人了,当然是为了活跃群气氛,也成了很多宅男的解闷的对象。我们今天的课题就是在自己的网站上搭建一个智能机器人的聊天窗口,当然数据不是我们自己编辑的,是从其他地方调用的,但是这样的一个项目也比较考验我们的前端知识的综合能力,特别是对于javascript的使用,包括ajax的封装等等,如果你的个人博客上也能有这样的一个人工机器人,我相信对于web前端的求职会有很大的帮助的!
如果想要更多的企业求职加分项目,案例,可以来一下我的前端群216634437,每天都会精挑细选一个特效,项目出来详细讲解,分享!
需要文档版源码来我的前端群216634437,已上传到群文件
智能机器人学习视频:HTML5+javascript大神带你开发人工智能机器人
头条号里有许多web前端学习视频,企业常用特效/案例/项目,敬请关注!
看完这张图有多少想去@baby Q的?
Django4.1环境和python3语言版本和html5语言和CSS3语言,重点参考github开源内容和Stackoverflow 的帖子内容,开发一个安全的web网站。请重点讲解下扩展auth_user表的方式和代码,扩展字段为单位名称、部门名称、小组名称、移动电话号码,其中扩展字段用户只能选择数据库中已有的单位名称、部门名称、小组名称,并且单位名称、部门名称、小组名称三个字段只有管理员可以在页面修改,请使用继承AbstractUser类的方式,不要使用profile方式,并且单位名称、部门名称、小组名称三个字段可以在页面单独修改,并在代码中添加注释内容,解释每一行代码的作用。
首先,我们需要在models.py中定义一个新的类来扩展AbstractUser类:
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
# 添加新的字段
unit_name = models.CharField(max_length=100, null=True, blank=True)
department_name = models.CharField(max_length=100, null=True, blank=True)
group_name = models.CharField(max_length=100, null=True, blank=True)
mobile_number = models.CharField(max_length=20, null=True, blank=True)
在这个新的CustomUser类中,我们添加了4个新的字段(unitname、departmentname、groupname和mobilenumber)来扩展AbstractUser类。这些新字段的意义分别是单位名称、部门名称、小组名称和移动电话号码。
接下来,我们需要创建一个新的表单类来在网站的页面上显示这些新的字段并且实现管理员可以修改这些字段的功能。这里我们将使用Django的ModelForm来简化表单的创建过程,同时也可以保证表单的安全性。
from django import forms
from .models import CustomUser
class CustomUserForm(forms.ModelForm):
# 定义表单中的字段
unit_name = forms.CharField(max_length=100, required=False)
department_name = forms.CharField(max_length=100, required=False)
group_name = forms.CharField(max_length=100, required=False)
mobile_number = forms.CharField(max_length=20, required=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 设置单位名称、部门名称、小组名称三个字段只能选择数据库中已有的名称
self.fields['unit_name'].widget.attrs['readonly'] = True
self.fields['department_name'].widget.attrs['readonly'] = True
self.fields['group_name'].widget.attrs['readonly'] = True
self.fields['unit_name'].choices = self.get_unit_name_choices()
self.fields['department_name'].choices = self.get_department_name_choices()
self.fields['group_name'].choices = self.get_group_name_choices()
# 获取单位名称、部门名称、小组名称的选项列表
def get_unit_name_choices(self):
# 从数据库中获取已有的单位名称列表
return [(unit_name, unit_name) for unit_name in CustomUser.objects.values_list('unit_name', flat=True).distinct()]
def get_department_name_choices(self):
# 从数据库中获取已有的部门名称列表
return [(department_name, department_name) for department_name in CustomUser.objects.values_list('department_name', flat=True).distinct()]
def get_group_name_choices(self):
# 从数据库中获取已有的小组名称列表
return [(group_name, group_name) for group_name in CustomUser.objects.values_list('group_name', flat=True).distinct()]
# 定义表单的Meta类
class Meta:
model = CustomUser
fields = ('username', 'email', 'first_name', 'last_name', 'unit_name', 'department_name', 'group_name', 'mobile_number')
在这个新的CustomUserForm表单类中,我们定义了4个字段(unitname、departmentname、groupname和mobilenumber),并且通过重载init方法来确保这些字段只能选择数据库中已有的名称。同时,我们还定义了一个Meta类来指定表单对应的模型类和需要显示的字段。
接下来,我们需要在网站的视图函数中使用这个CustomUserForm类来实现修改用户信息的功能。这里我们将使用Django的generic view来简化视图函数的创建过程。
from django.shortcuts import render, redirect
from django.views.generic.edit import UpdateView
from django.urls import reverse_lazy
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from .models import CustomUser
from .forms import CustomUserForm
@method_decorator(login_required, name='dispatch')
class CustomUserUpdateView(UpdateView):
model = CustomUser
form_class = CustomUserForm
template_name = 'user_update.html'
success_url = reverse_lazy('user_update')
# 重载get_object方法以确保只有管理员可以访问该页面
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
if not self.request.user.is_staff:
# 如果当前用户不是管理员,则重定向到用户详情页面
return redirect('user_detail', pk=obj.pk)
return obj
在这个新的CustomUserUpdateView类中,我们使用了Django的UpdateView来处理用户信息的修改请求。我们指定了需要修改的模型类(CustomUser)、使用的表单类(CustomUserForm)、模板名称(userupdate.html)以及修改成功后重定向的URL地址(userupdate)。同时,我们还通过重载get_object方法来确保只有管理员可以访问该页面。
最后,我们需要在网站的urls.py文件中添加一个新的URL路由来将用户信息修改页面映射到CustomUserUpdateView视图类。
from django.urls import path
from .views import CustomUserUpdateView
urlpatterns = [
path('user/update/', CustomUserUpdateView.as_view(), name='user_update'),
# 其他URL路由...
]
这样,我们就完成了扩展authuser表的方式和代码。在这个新的CustomUser类中,我们添加了4个新的字段(unitname、departmentname、groupname和mobile_number)来扩展AbstractUser类。同时,我们使用了CustomUserForm类来创建一个表单来显示和修改这些新的字段,并使用了CustomUserUpdateView类来处理这个表单的提交。在这个过程中,我们也注意到了安全性的问题,并确保只有管理员可以访问用户信息修改页面,并限制了用户可以选择的单位名称、部门名称和小组名称的范围。
*请认真填写需求信息,我们会在24小时内与您取得联系。