加入了原创博客联盟 Thu, Aug 28. 2008
地图聚合的数据格式前三名:KML、GeoRSS 和 GeoJSON Thu, Aug 28. 2008
原文地址:3 Top Data Formats for Map Mashups: KML, GeoRSS and GeoJSON。
随着地图聚合给最终用户提供的一系列更广泛的工具和应用程序,它在完善程度和功能性两方面逐渐走向成熟。 因此,我们需要一些预定义好的方法在传统的地理空间数据和新一代的地图聚合之间交换、发布这些地理空间数据,并且使用一种对 web 友好的方式使用这些数据。
为了满足这种需求,出现了一些新的地理空间数据格式,这能够让更大范围的用户和开发者来聚合地理相关的信息。 下面是当前可供从事地理信息聚合的开发者使用的三种主要数据格式的一个概括:
KML
你知道 Google 地球的前身,那个流行的名为 Keyhole 的三维地球浏览器吗? 如果你知道,那么这个基于 XML 的,Google 地球自己的文件格式被叫做 KML,意为 Keyhole 标记语言,就不值得惊讶了。 在地理空间相关的网站上,KML 无处不在,KML 支持从类似 Google 地图、微软的虚拟地球这样商业化的地图 API 和 OpenLayers 这样开源的地图 API 中导入、导出数据。 今年早些时候,Google 把 KML 作为一种开放标准发布,并且被开放地理空间联盟 (OGC) 采用。 你可以研读最新的 KML 规范 (当前是 2.2 版) 或者学习如何让 KML 与 Google 地图 API、虚拟地球或者 OpenLayers 集成。
GeoRSS
GeoRSS 提供了一种在 RSS (或者 Atom) 种子里通过特定的编码来包含地理参考信息的方法。GeoRSS 站点上说:
RSS 和 Atom 作为一种发布、共享信息的方法,正在逐渐流行起来, 因此,使用互操作的方式描述位置信息,来让程序能够请求、聚合、共享、地图化地理标记过的种子变得益发重要。
嵌入 GeoRSS 非常简单,仅仅在每个条目中增加一个类似 <georss:point>45.256 -71.92</georss:point> 这样的元素就行了,这里使用的是简易GeoRSS 格式, 如果要需要复杂完整的编码格式,可以选择支持更多的特性的 GeoRSS-GML 格式。这两种 GeoRSS 格式都支持基本的地理特征 (点、线、边框和多边形)。 和 KML 一样,商业化的地图 API 和开源地图 API 都支持 GeoRSS,并且主要作为导入数据的格式使用。 GeoRSS 许诺对整合内容的会有更好的支持。
GeoJSON
GeoJSON 是基于 JavaScript 对象表示法 (JSON)的一种新的数据格式,用来对大量的地理特征进行编码,支持的地理特征有点、线、多边形、多多边形和地理信息集合。
{ "type": "Point", "coordinates": [43.542, -118.454] }
GeoJSON 可以被 JavaScript 简单、快速的解析,而且 GeoJSON 还提供了一个可以很容易的进行交换的轻量级数据格式。 自从 GeoJSON 正式发表 1.0 版后,GeoJSON 的魅力逐渐增加,得到了包括 FireEagle 和 OpenLayers 在内的一些流行的 API 的支持 (但是不确定将来是否能够得到类似 Google 地图或者虚拟地球这样的商业 API 的支持)。
注意 GeoRSS 和 GeoJSON 都采用创作共用授权协议授权。
我们饶有兴趣的看着这些格式如何发展,而且很想知道类似 GeoRSS 和 GeoJSON 这样的格式能否得到地图 API 和地图聚合开发者的采用。
解决 propel-convert-xml-schema 把 null 默认值处理为字符串的问题 Thu, Aug 21. 2008
通过 propel-convert-xml-schema 把 xml schema 转化成 PHP 代码的时候,如果一个字符型字段不能为空且没有明确输入默认值,例如:
- <column name="username" type="VARCHAR" size="128" required="true" default=""/>
生成的 PHP 代码中该字段的默认值是 'null' 而不是 null,导致页面输入框的默认值不是空值,而是一个字符串的 null,很恼火。因为要么在页面中每个字段都对 'null' 做特殊处理,要么修改生成的 PHP 类文件,但是一旦重新执行 propel-convert-xml-schema 就会重新覆盖。
解决办法:修改 propel-generator/classes/propel/phing/PropelCreoleTransformTask.php 文件(对于 Symfony 来说,就是 symfony/vendor/propel-generator/classes/propel/phing/PropelCreoleTransformTask.php)。应用如下 patch:
- 507,510c507
- < //$node->setAttribute("default", iconv($this->dbEncoding, 'utf-8', $defValue));
- < if(!empty($defValue)) {
- < $node->setAttribute("default", iconv($this->dbEncoding, 'utf-8', $defValue));
- < }
- ---
- > $node->setAttribute("default", iconv($this->dbEncoding, 'utf-8', $defValue));
参见:#2045 (propel-convert-xml-schema generate null text as default values) - symfony - Trac。
在 Symfony 中结合使用 Structures/DataGrid、sfSmartView、Propel Mon, Aug 18. 2008
今天需要在 symfony 中使用 datagrid 型控件对一个分页+排序的表格进行展示。但是 sfDataGrid 写得实在是太糟糕了,没有找到其他的比较好的 plugin。
PEAR 中的 Structures/DataGrid 倒是不错。但是两个问题:无法和现在偶使用的 sfSmartyView 直接结合,因为在 $datagrid->fill($smarty); 的时候,symfony 还没有生成 view instance,除非直接获取生成的 HTML_TABLE 或者类似的结果,这样不利于 MVC 的分离和对具体样式的控制;二是现有的 datasource 无法和 symfony 使用的 propel 直接结合,如果采用检索全部结果再传递给 Structures/DataGrid 进行分页处理,那么效率非常低下。
于是自己写一个 datasource,并且使用了一点技巧解决和 sfSmartyView 结合的问题。
Structures_DataGrid_DataSource_Propel 类的定义:
- class Structures_DataGrid_DataSource_Propel extends Structures_DataGrid_DataSource {
- protected $criteria = null;
- protected $con = null;
- protected $object = null;
- function __construct($object, $criteria, $con = null) {
- parent::Structures_DataGrid_DataSource();
- $this->object = new $object();
- $this->criteria = $criteria;
- $this->con = $con;
- }
- //TODO: this is not a good way, due to MySQL Bug #21787
- $this->criteria->setLimit(0);
- $this->criteria->setOffset(0);
- $result = $this->object->doCount($this->criteria);
- return $result;
- }
- case 'asc' :
- $this->criteria->addAscendingOrderByColumn($sortSpec);
- break;
- case 'desc' :
- $this->criteria->addDescendingOrderByColumn($sortSpec);
- break;
- default :
- return PEAR::raiseError("Sorting dir must be ASC or DESC!");
- }
- return true;
- }
- function fetch($offset = 0, $len = null) {
- $c = $this->criteria;
- $c->setOffset($offset);
- $c->setLimit($len);
- }
- $objects = $this->object->doSelect($c);
- foreach ($objects as $object) {
- $result[] = $object->toArray(BasePeer::TYPE_COLNAME);
- }
- return $result;
- }
- }
action 中的代码段:
- public function execute() {
- // get data source
- $c = new Criteria();
- $datasource = new Structures_DataGrid_DataSource_Propel('SysUiaAccountPeer', $c);
- // setup datagrid
- $datagrid = & new Structures_DataGrid(10);
- $datagrid->bindDataSource($datasource);
- SysUiaAccountPeer::USERNAME=>"DESC"
- ));
- $datagrid->addColumn(new Structures_DataGrid_Column('用户名', SysUiaAccountPeer::USERNAME, SysUiaAccountPeer::USERNAME, array (), ' ', ''));
- // add more columns here
- // use a tip to get output using smarty
- $smarty = $datagrid->setRenderer('smarty');
- // set pager params
- 'prevImg'=>"上一页", 'nextImg'=>"下一页", 'separator'=>"|", 'delta'=>"5", 'clearIfVoid'=>true, 'spacesBeforeSeparator'=>"1", 'spacesAfterSeparator'=>"1", 'firstPageText'=>'第一页', 'lastPageText'=>'最后页'
- ), $smarty);
- $this->dg = $datagrid->getOutput();
- $this->pager = $pager;
- return sfView::SUCCESS;
- }
模板中的处理:
- <!-- Build header -->
- {section name=col loop=$dg.columnSet}
- <th{$dg.columnSet[col].attributes}><!-- Check if the column is sortable -->
- {if $dg.columnSet[col].link != ""} <a href="{$dg.columnSet[col].link}">{$dg.columnSet[col].label}</a>
- <!-- Show the current ordering with an arrow --> {if
- $dg.columnSet[col].name == $smarty.request.orderBy} {if
- $smarty.request.direction == "ASC"}↑{elseif
- $smarty.request.direction == "DESC"}↓{/if} {/if} {else}
- {$dg.columnSet[col].label} {/if}</th>
- {/section}
- </tr>
- <!-- Build body -->
- {section name=row loop=$dg.recordSet}
- <tr {if $smarty.section.row.iteration is even}bgcolor="#EEEEEE"{/if}>
- {section name=col loop=$dg.recordSet[row]}
- <td{$columnSet[col].attributes}>{$dg.recordSet[row][col]}</td>
- {/section}
- </tr>
- {/section}
- <td colspan="...">{$pager}</td>
- </tr>
- </table>
MySQL 对于 COUNT(*) 和 LIMIT 同时使用的 BUG Sun, Aug 17. 2008
对一个有 29 条记录的表进行如下操作:
- SELECT COUNT( sys_uia_account.ID ) FROM sys_uia_account结果 29
- SELECT COUNT( sys_uia_account.ID ) FROM sys_uia_account LIMIT 10结果 29
- SELECT COUNT( sys_uia_account.ID ) FROM sys_uia_account LIMIT 10 , 10没有结果
看来以后使用 COUNT(*) 的时候不能同时使用 LIMIT offset, row_count 这种表达方式了。
PS:如果统计全表记录,使用下面的方法会更加快捷:
- SELECT SQL_CALC_FOUND_ROWS *
- FROM sys_uia_account;
- SELECT FOUND_ROWS( ) ;
唯一需要注意的就是,这两个查询必须在一起进行,因为 SQL_CALC_FOUND_ROWS 不对结果进行任何缓存。

