2011年11月29日星期二

Google Maps JavaScript API 几种事件(event)的说明

当移动或放大缩小google maps时,将会产生zoom_changed、center_changed、bounds_changed、dragend等各种事件。通过下面的html代码可以测试这些事件何时发生,以及发生的先后顺序。

运行html文件后可得出下面的结论:

  1. 当移动地图时,center_changed和bounds_changed事件会不断触发,一直到移动结束。所以如果需要在视图改变时利用Ajax方式获取数据时千万不要绑定到bounds_changed(或center_changed)事件,因为这样会在移动一次地图时触发多次bounds_changed事件,导致多次重复的Ajax请求

  2. 事件触发的顺序 center_changed -- zoom_changed -- bounds_changed , 当center_changed事件发生时不一定能正确通过getBounds()函数获取视图边界

  3. dragend事件在拖动结束时触发,此时由于地图可能还在移动,所以还可能继续触发center_changed和bounds_changed事件







Google Maps JavaScript API v3 Example: Event Simple

















2011年11月25日星期五

jqPlot选项结构

jqPlot是一个非常易用和强大的jQuery绘图插件,可以绘制曲线图、饼状图、柱状图等,支持多达9个Y坐标轴,能自动生成趋势线,可自定义坐标系、坐标轴、线条类型颜色、标签方向、图例、高亮数据点、鼠标提示字符串格式等各个绘图细节。官方网站为www.jqplot.com ,基本使用步骤见http://www.jqplot.com/docs/files/usage-txt.html

绘制图形的语句为 chart = $.jqplot('chart', data, optionsObj);
$.jqplot函数有三个参数,第一个为div的ID,第二个为数据,第三个为绘图选项。

选项对象的结构如下:

jqPlot-|
|-seriesColors(Array)
|-textColor
|-fontFamily
|-fontSize
|-stackSeries
|-series(Array)-|
| |-Series1-|
| | |-lineWidth
| | |-shadow
| | |-showLine
| | |-showMarker
| | |-color
| | |-renderer
| | |-rendererOptions
| | |-trendline
| |-Series2...
| |-...
| |-SeriesN
|
|-grid(Object)-|
| |-drawGridLines
| |-background
| |-borderColor
| |-borderWidth
| |-shadow
|
|-title(Object)-|
| |-text
| |-show
| |-fontFamily
| |-fontSize
| |-textAlign
| |-textColor
|
|-axes(Object)-|
| |-xais-|
| | |-min
| | |-max
| | |-numberTicks
| | |-showTicks
| | |-showTickMarks
| | |-pad
| | |-ticks
| | |-tickOptions
| | |-renderer
| | |-rendererOptions
|
|-legend-|
| |-show
| |-location
|
|-cursor
|
|-dragable
|
|-highlighter
|
| ... and so on

2011年11月23日星期三

Sina App Engine(SAE)中的Mysql服务限制说明

Mysql服务概要

SAE平台为每个App支持几乎所有MySQL的特性。目前支持MyISAM引擎,暂不支持InnoDB。需要注意的是SAE的数据库需要显式开启或者禁用。您还可以通过phpmyadmin来创建数据库和数据表。SAE的PHP环境提供了标准的MYSQL,MYSQLI和PDO模块(基于MySQLnd),您可以直接使用这三个模块来操作您的数据库, 您可以使用预定义常量来连接数据库,我们不排除未来可能调整数据库端口,使用预定义常量能够避免这样的问题。当然SAE也提供了SaeMysql类,我们也推荐您使用这个类来操作MySQL资源。

服务限制














































































限制 相关错误信息 数值
单表的最大行数 Table too many rows 10 000 000 行
库的最大表数量 Too many tables 512个
不支持的存储引擎类型 Not support table type memory temporary
最大外排序的行数 Filesort on too many rows 65536 行
最大无索引的操作行数 100 000 行
查询的最大操作行数 Select on too many rows 1 000 000 行
更新的最大操作行数 Update on too many rows 1 000 000 行
删除的最大操作行数 Delete on too many rows 1 000 000 行
创建索引时允许的表的最大行数 Create index on big table 500 000 行
修改表结构时允许的表的最大行数 Alter table on big table 500 000 行
SQL并发执行时间和(读库) Operations take too much time cost 500 000 毫秒
SQL并发执行时间和(写库) Operations take too much time cost 200 000 毫秒
警报阈值百分比 80%
表主键及聚簇索引奖励系数 1024 倍


MySQL慢查询配额

SQL执行时间超过1秒,即为慢查询,其分钟配额为
条目数 10
累计执行时间 30秒
扫描行 1,000,000

分钟配额
运行在SAE上的应用(App)将会消耗平台资源,为保证各App不互相影响,我们引入了分钟配额的概念,即:在每分钟内每个应用的各个服务所消耗的 资源的速度。比如,当平台中的某个应用的MySQLl服务一分钟内累计请求数达到20万,或者流出宽带超过600M,或者累计CPU执行时间超过400s,我们将会立即禁掉该应用的MySQL服务,禁用五分钟后,恢复会自动恢复,避免影响到SAE平台的稳定性。服务因为超过“分钟配额”而被禁用时,会在“服务状态”看到该服务被禁用的原因。

注意:表中红色配额值是自2011-11-3号,即SAE两周年起开始执行的新配额值,旧配额值将不再采用。





































































































服务 请求数 cpu时间 流入流量 流出流量
HTTP 200,000=>500,000 300s=>600s 300MB=>1500MB 300MB=>1500MB
HTTPS 50,000=>1,000,000 50s=>100s 10MB=>100MB 10MB=>100MB
MySQL 200,000 400s=>600s 300MB=>600MB 600MB=>1200MB
Memcache 300,000 NA 150MB=>300MB 150MB=>300MB
Fetchurl 50,000=>100,000 NA 100MB=>200MB 100MB=>200MB
Image 5,000 300s 150MB 150MB
Storage 5,000 NA 50MB=>80MB 400MB
Mail 50=>500 NA 20MB 20MB
Cron/offset 50 50s 1MB 1MB
Cron/nooffset 200 30s=>60s 5MB 5MB
TaskQueue 20,000=>40,000 60s=>100s 10MB=>20MB 10MB=>20MB
VerifyCode 100 4s 2MB 2MB
KVDB 150,000=>300,000 NA 150MB=>300MB 150MB=>300MB


容量配额

容量配额是针对MySQL\Storage\MemCache设置的,是指用户能够使用的磁盘或内存的最大限制(见下表),其中Memcache的最大容量配额是用户自己在初始化Memcache时设置的,可设置的范围是1M~256M。
服务 配额
MySQL 5G
Storage 10G*
KVDB 100G
*每个App允许建立5个Storage domain 每个domain为2G

2011年11月15日星期二

查看全国各地天气实况信息(2011年11月25日更新)

每天我们都会从各种渠道获取天气预报信息,比如电视、手机短信、互联网查询等。我们也经常发现气象预报与实际的天气会有一些差异。那么每天是否有人工观测的天气实况信息呢,答案是肯定的。

全国有几千个气象观测站,很多观测站是每隔3小时都会有人工去观测一次,记录观测时的实际天气状况、气温、气压、刚过去几小时的天气状况、能见度等信息。

现在你可以通过下面的网站来查看这些信息:全国各地天气实况

全国各地天气实况

这个网站将现在全国各地的天气实况记录信息显示在地图上,移动地图即可查看各地天气实况。

有时我们也希望查询过去某天的实际天气状况,比如一个月前是什么天气。随后将推出查询过去天气信息的功能。
-----------------------------------------------------------------------------------------------
已添加2010年11月到现在全国各地区的观测数据。现在你可以在这个网站上查询一年内任意一天的观测数据。

2011年11月13日星期日

CSS绝对定位的一些解释






absolute定位实验







absoluteA
absoluteB
floatC
floatD






效果如下:
css定位

可以看到:

  1. 采用absolute定位时,left,right,top,bottom分别是定位元素与父元素 左-左 右-右 上-上 下-下 边的距离

  2. float元素的父元素为非绝对定位元素时,float不会撑大父元素的高度。当float元素的父元素为绝对定位元素时,float元素会撑大父元素的宽度和高度

  3. 当absolute定位的元素(比如元素M)内容改变时,定位属性不会改变。比如指定M的bottom为10px,当动态向M添加了一些内容导致M高度增大时,M将向上面伸展,底框与父元素始终保持10px

2011年11月10日星期四

C++ 以POST方式向网页提交数据

示例代码如下:


#include
#include
#include //定义了MFC CInternetSession类等

bool PostHttpPage(const std::string& hostName, const std::string& pathName, const std::string& postData)
{
using namespace std;

CInternetSession session("your app agent name");

try
{
INTERNET_PORT nPort = 80;
DWORD dwRet = 0;

CHttpConnection* pServer = session.GetHttpConnection(hostName.c_str(), nPort);
CHttpFile* pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, pathName.c_str());

CString strHeaders = "Content-Type: application/x-www-form-urlencoded"; // 请求头

//开始发送请求

pFile->SendRequest(strHeaders,(LPVOID)postData.c_str(),postData.size());
pFile->QueryInfoStatusCode(dwRet);


if (dwRet == HTTP_STATUS_OK)
{
CString result, newline;

while(pFile->ReadString(newline))
{//循环读取每行内容
result += newline+"\r\n";
}

std::cout< }
else
{
return false;
}
delete pFile;
delete pServer;

}
catch (CInternetException* pEx)
{
//catch errors from WinInet
TCHAR pszError[200];
pEx->GetErrorMessage(pszError, 200);

std::cout< return false;
}
session.Close();

return true;
}

int main(void)
{
//向http://current.sinaapp.com/post.php发送数据
PostHttpPage("current.sinaapp.com","post.php","name=rain&age=12");
}

C++程序读取和保存配置信息

虽然MSDN推荐使用注册表代替ini文件来保存程序配置和运行信息,但一般的小程序还是用ini文件来保存信息比较方便一些。

微软提供了GetPrivateProfile和WritePrivateProfile开头的一组函数来读取和保存ini文件。
比如对于名称为scan.ini的配置文件

[lastest]
year =2011
month =11
day =8
hour =19
minute =0
second =56


读取时间信息的代码如下

CTime get_last_time()
{
int year,month,day,hour,minute,second;

year = GetPrivateProfileInt("lastest","year",1970,"./scan.ini");
month = GetPrivateProfileInt("lastest","month",1,"./scan.ini");
day = GetPrivateProfileInt("lastest","day",2,"./scan.ini");
hour = GetPrivateProfileInt("lastest","hour",0,"./scan.ini");
minute = GetPrivateProfileInt("lastest","minute",0,"./scan.ini");
second = GetPrivateProfileInt("lastest","second",0,"./scan.ini");

return CTime(year,month,day,hour,minute,second);
}


写入时间信息的代码如下

bool set_latest_time(const CTime &latest)
{
BOOL year,month,day,hour,minute,second;

year = WritePrivateProfileString("lastest","year",latest.Format("%Y"),"./scan.ini");
month = WritePrivateProfileString("lastest","month",latest.Format("%#m"),"./scan.ini");
day = WritePrivateProfileString("lastest","day",latest.Format("%#d"),"./scan.ini");
hour = WritePrivateProfileString("lastest","hour",latest.Format("%#H"),"./scan.ini");
minute = WritePrivateProfileString("lastest","minute",latest.Format("%#M"),"./scan.ini");
second = WritePrivateProfileString("lastest","second",latest.Format("%#S"),"./scan.ini");

return year && month && day && hour && minute && second;
}

2011年11月9日星期三

CPP程序监视plot文件夹并以post方式上传数据


//#include // MFC 核心和标准组件

#include
#include
#include
#include
#include

void display_post_result(std::string result)
{
std::cout<
std::ofstream fout("log-info.txt",std::ios_base::out|std::ios_base::app);
if(!fout.is_open())
{
std::cout<<"打开或创建文件log-info.txt失败!";
return;
}
CTime now = CTime::GetCurrentTime();

fout<}

bool PostHttpPage(const std::string& hostName, const std::string& pathName, const std::string& postData)
{
using namespace std;

CInternetSession session("Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2) Gecko/20100115 Firefox/3.6");

try
{
INTERNET_PORT nPort = 80;
DWORD dwRet = 0;

CHttpConnection* pServer = session.GetHttpConnection(hostName.c_str(), nPort);
CHttpFile* pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, pathName.c_str());

CString strHeaders = "Content-Type: application/x-www-form-urlencoded"; // 请求头

display_post_result("posting data...");

pFile->SendRequest(strHeaders,(LPVOID)postData.c_str(),postData.size());
pFile->QueryInfoStatusCode(dwRet);


if (dwRet == HTTP_STATUS_OK)
{
CString result, newline;

while(pFile->ReadString(newline))
{
result += newline+"\r\n";
}

display_post_result(("back:"+result).GetBuffer());
}
else
{
display_post_result("post 失败!");
return false;
}
delete pFile;
delete pServer;

}
catch (CInternetException* pEx)
{
//catch errors from WinInet
TCHAR pszError[200];
pEx->GetErrorMessage(pszError, 200);

display_post_result(pszError);
return false;
}
session.Close();

return true;
}

bool deal_plot_file(std::string orgine_name, std::string &result)
{
std::ifstream fin(orgine_name.c_str());
if(!fin.is_open())
{
display_post_result("打开文件"+orgine_name+"失败!");
return false;
}

display_post_result("dealing "+orgine_name+"...");

result = "";
std::string linestr, number;

std::istringstream stringin;
while(std::getline(fin,linestr))
{
stringin.clear();
stringin.str(linestr);
if(stringin>>number)
{
if(number.size() == 5)
{
result += number;
//读取并丢弃经纬度高度和站点级别信息
stringin>>number>>number>>number>>number;

while(stringin>>number)
{
result += ' ' + number;
}
//result += "\r\n";

}
else
{
result += linestr + "\r\n";
}
}
}

return true;
}

CTime get_last_time()
{
int year,month,day,hour,minute,second;

year = GetPrivateProfileInt("lastest","year",1970,"./scan.ini");
month = GetPrivateProfileInt("lastest","month",1,"./scan.ini");
day = GetPrivateProfileInt("lastest","day",2,"./scan.ini");
hour = GetPrivateProfileInt("lastest","hour",0,"./scan.ini");
minute = GetPrivateProfileInt("lastest","minute",0,"./scan.ini");
second = GetPrivateProfileInt("lastest","second",0,"./scan.ini");

return CTime(year,month,day,hour,minute,second);
}

bool set_latest_time(const CTime &latest)
{
BOOL year,month,day,hour,minute,second;

year = WritePrivateProfileString("lastest","year",latest.Format("%Y"),"./scan.ini");
month = WritePrivateProfileString("lastest","month",latest.Format("%#m"),"./scan.ini");
day = WritePrivateProfileString("lastest","day",latest.Format("%#d"),"./scan.ini");
hour = WritePrivateProfileString("lastest","hour",latest.Format("%#H"),"./scan.ini");
minute = WritePrivateProfileString("lastest","minute",latest.Format("%#M"),"./scan.ini");
second = WritePrivateProfileString("lastest","second",latest.Format("%#S"),"./scan.ini");

return year && month && day && hour && minute && second;
}

std::string get_plot_dir()
{
char dir_buf[1024] ;
DWORD result = GetPrivateProfileString("path","plotdir","Z:/surface/plot/",dir_buf,1000,"./scan.ini");

return dir_buf;
}

bool scan_plot_dir()
{
CTime last_time = get_last_time();

CFileFind finder;

std::string dir = get_plot_dir();
if('\\' != dir[dir.size()-1 ] && '/' != dir[dir.size()-1 ] )
{
dir += '/';
}
BOOL bWorking = finder.FindFile( (dir+ "*.000").c_str() );

CTime lastest(1), ftime;
std::string result;
//

while (bWorking)
{
bWorking = finder.FindNextFile();

if(finder.GetLastWriteTime(ftime)
&& ftime > last_time
&& deal_plot_file(finder.GetFilePath().GetBuffer(), result)
)
{
bool is_post_success =
PostHttpPage("current.sinaapp.com","update-mysql-from-post.php","app-content="+result);

if(!is_post_success)
{//有一次post不成功就返回本次dir scan
display_post_result("post maybe failure...");
return false;
}

lastest = ftime > lastest ? ftime : lastest;

display_post_result("wait 2 minute...");
Sleep(2*1000*60);//post成功时等待2分钟
display_post_result("continue next file find...");
}
}

if(CTime(1) != lastest )
{
return set_latest_time(lastest);
}

return false;
}

int main(void)
{
std::cout<<"version 1.1 2011-11-09\n"
<<"-------------------------------------\n";
while(true)
{
scan_plot_dir();

display_post_result("wait 5 minute...");
Sleep(5*1000*60);//每次dir scan完成时等待5分钟
display_post_result("continue next plot dir scan...");
}

}

C++ 获取文件修改时间等信息

可以利用MFC中的文件查找类CFileFind来获取文件或文件夹的修改时间访问时间等信息


#include // MFC 核心和标准组件
#include
#include

void get_file_info(const std::string &orgine_name)
{
CFileFind finder;
BOOL bWorking = finder.FindFile(orgine_name.c_str());

while (bWorking)
{
bWorking = finder.FindNextFile();
CTime ftime;

std::cout<<"\nGetFileName: "< <<"\nGetFilePath: "< <<"\nGetFileTitle: "< <<"\nGetFileURL: "< <<"\nIsDirectory: "<

finder.GetCreationTime(ftime);
std::cout<<"\nGetCreationTime:"<
finder.GetLastWriteTime(ftime);
std::cout<<"\nGetLastWriteTime: "<
finder.GetLastAccessTime(ftime);
std::cout<<"\nGetLastAccessTime:"<
std::cout< }

}

int main(void)
{
get_file_info("Z:/surface/plot");
get_file_info("Z:/surface/plot/11110911.000");
}


运行上面代码输出如下:

GetFileName: plot
GetFilePath: Z:\surface\plot
GetFileTitle: plot
GetFileURL: file://Z:\surface\plot
IsDirectory: 1
GetCreationTime:[2011-05-22 10:10:45]
GetLastWriteTime: [2011-11-09 11:30:55]
GetLastAccessTime:[2011-11-09 12:30:57]

GetFileName: 11110911.000
GetFilePath: Z:\surface\plot\11110911.000
GetFileTitle: 11110911
GetFileURL: file://Z:\surface\plot\11110911.000
IsDirectory: 0
GetCreationTime:[2011-11-09 11:30:55]
GetLastWriteTime: [2011-11-09 13:00:54]
GetLastAccessTime:[2011-11-09 13:00:54]
请按任意键继续. . .

2011年11月7日星期一

jQuery 1.7 事件绑定函数

jQuery 1.7 前几天发布了,新增了两个事件处理函数 .on().off() 分别用来绑定和解除事件。这两个函数可以取代之前版本中所有的事件处理函数。
现在事件绑定和解除更简单了,下面是新旧版本事件处理代码的对比:

$('a').bind('click', myHandler);
$('a').on('click', myHandler);

$('form').bind('submit', { val: 42 }, fn);
$('form').on('submit', { val: 42 }, fn);

$(window).unbind('scroll.myPlugin');
$(window).off('scroll.myPlugin');

$('.comment').delegate('a.add', 'click', addNew);
$('.comment').on('click', 'a.add', addNew);

$('.dialog').undelegate('a', 'click.myDlg');
$('.dialog').off('click.myDlg', 'a');

$('a').live('click', fn);
$(document).on('click', 'a', fn);

$('a').die('click');
$(document).off('click', 'a');


另外想说一下jQuery中的事件对象(Event Object)

jQuery的事件系统规范化了事件对象(根据W3C标准)。jQuery保证事件对象被传递给事件处理程序。大部分的属性从原来的事件中复制和规范化给新的事件对象。

jQuery保证下列属性是事件对象的成员,尽管它们中的一些值可能是undefined(依赖于具体的事件):

altKey, attrChange, attrName, bubbles, button, cancelable,
charCode, clientX, clientY, ctrlKey, currentTarget, data, detail,
eventPhase, fromElement, handler, keyCode, layerX, layerY,
metaKey, newValue, offsetX, offsetY, originalTarget,
pageX, pageY, prevValue, relatedNode, relatedTarget,
screenX, screenY, shiftKey, srcElement, target,
toElement, view, wheelDelta, which


为了跨浏览器兼容,jQuery 对下列属性进行了规范化:

target
relatedTarget
pageX
pageY
which
metaKey


特别注意,并不是所有原始事件的属性都被复制给jQuery的事件对象。这时可以用 event.originalEvent 来获取原始事件对象。比如在HTML5文件拖放操作时需要用到drop事件的dataTransfer属性,可以像下面这样做:

$('body').on('drop',function(e){
var file = e.originalEvent.dataTransfer.files[0];//获取一组拖放文件中的第一个文件

reader = new FileReader();//创建FileReader对象来读取文件内容
reader.onload = function(e){//读取完成时将内容显示于body中
$('body').text(e.target.result);
};
reader.readAsText(file,'utf-8');//开始读取文件

//相当于同时调用e.stopPropagation() 和 e.preventDefault()
return false;//阻止事件冒泡和浏览器的默认行为
});

2011年11月6日星期日

Google Maps 显示地面天气实况

一、用到的技术
1.Google Maps API
2.jQuery & jQuery UI &jqPlot
3.canvas 绘制风速及UI
4.mysql 站点信息表 & 站点-时间-观测数据 表 &其他信息表(站点和数据最近更新时间、最新数据时间)
5.php返回json形式站点信息并在客户端持久存储
6.Ajax轮询请求获取最新数据
7.GeoLocation获取用户地理位置并定位
8.鼠标悬停站点时显示基本信息,点击detail时Ajax获取本站点最详尽信息
9.Javascript解析数据代码为天气信息,计算湿度和本站气压等
10.统计某一段时间数据(单站数据或多站对比)
11.C++ 自动监测处理(去掉站点经纬度高度等信息)地面图文件并post数据 & 网页手动上传文件更新(提供基本上传和拖动上传方式)

地面填图文件示例:11110620.000

二、代码实现
1.创建站点数据表

CREATE TABLE IF NOT EXISTS station(
id INT UNSIGNED NOT NULL UNIQUE PRIMARY KEY ,
lon DOUBLE NOT NULL ,
lat DOUBLE NOT NULL ,
height DOUBLE NOT NULL ,
LEVEL TINYINT UNSIGNED NOT NULL ,
name VARCHAR( 50 )
);

2.创建地面观测数据表

CREATE TABLE IF NOT EXISTS surface(
recordId int(10) unsigned NOT NULL AUTO_INCREMENT,
stationId int(10) unsigned NOT NULL,
recordTime datetime NOT NULL,
totalCloudAmount smallint(5) unsigned NOT NULL,
windDirection smallint(5) unsigned NOT NULL,
windSpeed smallint(5) unsigned NOT NULL,
seaLevelPressure smallint(5) unsigned NOT NULL,
press3hour smallint(6) NOT NULL,
pastWeather1 smallint(5) unsigned NOT NULL,
pastWeather2 smallint(5) unsigned NOT NULL,
hour6precipitation double NOT NULL,
lowCloudAmount smallint(5) unsigned NOT NULL,
lowCloudShape smallint(5) unsigned NOT NULL,
lowCloudHight smallint(5) unsigned NOT NULL,
dewpoint double NOT NULL,
visibility double unsigned NOT NULL,
presentWeather smallint(5) unsigned NOT NULL,
temperature double NOT NULL,
middleCloudShape smallint(5) unsigned NOT NULL,
highCloudShape smallint(5) unsigned NOT NULL,
transformT24 smallint(6) NOT NULL,
transformPress24 smallint(6) NOT NULL,
PRIMARY KEY (recordId),
UNIQUE KEY stationId (stationId,recordTime)
) ENGINE=MyISAM DEFAULT CHARSET=utf8


每个月数据约100万行,可每个月建立一张表

CREATE TABLE IF NOT EXISTS `surface201111` LIKE surface;


3.插入数据到station表

function update_stations($content){
preg_match_all('/^(\d{5})\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)\s+(-?\d+)\s+(\d+)/m',$content,$matchs,PREG_SET_ORDER);

//print_r($matchs);

$sql = 'INSERT IGNORE INTO station (id, lon, lat, height, level) VALUES ';

for($i = 0; $i < count($matchs); $i++){
$sql .= '( ';

for($j = 1; $j < 5; $j++){
$sql .= $matchs[$i][$j].",";
}
$sql .= $matchs[$i][5] .' )';
if($i < count($matchs) - 1)
{
$sql .= ',';
}
}
//echo $sql;

$mydb = new DB(SAE_MYSQL_HOST_M);
return $mydb -> doSql($sql);
}

4.插入数据到surface表

function getSurfacePatten(){
$patten = '/^(\d{5})(?:\s+-?\d+\.\d+){2}(?:\s+-?\d+\s+\d+)';

$patten .= '\s+(\d+)';
$patten .= '\s+(\d+)';
$patten .= '\s+(\d+)';
$patten .= '\s+(\d+)';
$patten .= '\s+(-?\d+)';
$patten .= '\s+(\d+)';
$patten .= '\s+(\d+)';

$patten .= '\s+(\d*\.\d+|\d+)';

$patten .= '\s+(\d+)';
$patten .= '\s+(\d+)';
$patten .= '\s+(\d+)';

$patten .= '\s+(-?\d*\.\d+|\d+)';
$patten .= '\s+(\d*\.\d+|\d+)';
$patten .= '\s+(\d+)';
$patten .= '\s+(-?\d*\.\d+|\d+)';

$patten .= '\s+(\d+)';
$patten .= '\s+(\d+)';

$patten .= '\s+\d+';
$patten .= '\s+\d+';

$patten .= '\s+(-?\d+)';
$patten .= '\s+(-?\d+)';

$patten .= '/m';

return $patten;
}

function update_surface($content){

preg_match('/(\d+)\s+(0?\d+)\s+(0?\d+)\s+(0?\d+)\s+(\d+)\s*$/m',$content,$matchs);

$year = $matchs[1] > '50' ? '19'.$matchs[1] : '20'.$matchs[1];
$timestr = $year.$matchs[2].$matchs[3].$matchs[4].'0000';

$patten = getSurfacePatten();

preg_match_all($patten,$content,$matchs,PREG_SET_ORDER);

//print_r($matchs);

$sql = 'INSERT IGNORE INTO surface (';
$sql .= 'recordTime,stationID,';
$sql .= 'totalCloudAmount,windDirection,windSpeed,seaLevelPressure,press3hour,pastWeather1,pastWeather2,';
$sql .= 'hour6precipitation,lowCloudAmount,lowCloudShape,lowCloudHight,';
$sql .= 'dewpoint,visibility,presentWeather,temperature,';
$sql .= 'middleCloudShape,highCloudShape,';
$sql .= 'transformT24,transformPress24';
$sql .= ' ) VALUES ';

$mydb = new DB(SAE_MYSQL_HOST_M);

for($i = 0; $i < count($matchs); $i++){
$isexist = $mydb -> rows_of_select('select * from surface where stationId = '.$matchs[$i][1].' and recordTime = '.$timestr);

if($isexist > 0) continue;

$sql .= '( ';
$sql .= $timestr.",";

for($j = 1; $j < 20; $j++){
$str = $matchs[$i][$j].",";
$sql .= $str[0] === '.' ? '0'.$str : $str;
}
$sql .= $matchs[$i][20] .' )';
if($i < count($matchs) - 1)
{
$sql .= ', ';
}
}


return $mydb -> doSql($sql);
}

5.更新站点信息(站点名称)

update station set name = '顺义' where id=54398;
update station set name = '海淀' where id=54399;
update station set name = '延庆' where id=54406;
update station set name = '佛爷顶' where id=54410;
update station set name = '汤河口' where id=54412;
update station set name = '密云' where id=54416;
update station set name = '怀柔' where id=54419;
update station set name = '密云上甸子' where id=54421;
update station set name = '平谷' where id=54424;
update station set name = '通州' where id=54431;
update station set name = '朝阳' where id=54433;
update station set name = '昌平' where id=54499;
update station set name = '斋堂' where id=54501;


6.根据用户请求返回数据:

SELECT id, lon, lat,
LEVEL
FROM station
WHERE LEVEL <=2
ORDER BY sqrt( lon * lon + lat * lat )
LIMIT 30


7.检查是否有重复数据

SELECT recordTime, stationId, count( recordTime ) AS num
FROM `surface`
WHERE recordTime =20111111230000
GROUP BY stationId
LIMIT 500


8.添加唯一性约束条件

delete FROM `surface`
WHERE stationId =99999;

alter table surface
add unique (stationId,recordTime);