Posts Tagged ‘GIS’

Perfomed my first commit to OpenStreetMap.org

06月 13, 2010

每每看到OSM上南京的地图都一阵唏嘘,这么多年了几乎没有任何变化,仅有的一些数据也都是错误百出,身为一个南京人,又是一个GIS专业毕业的,深感压力巨大。

实际上编辑OSM没有大家想象的那么复杂。任何人都可以注册登录网站,用默认的Potlatch工具进行编辑,即使你手上没有数据也可以编辑一些基本的属性,比如把错误的中山路纠正为北京东路。我从MapMyTracks上导出GPX文件上传到OSM,GPS数据会在OSM上被显示出来,根据这些数据稍作加工就是道路信息了。浏览器里的编辑器功能并不强大,KDE桌面的Merkaator的编辑能力要强大得多,可以对道路进行split/join操作,这些操作对之前南京地图上各种错误的数据非常必要。

其实OpenStreetMap可以借助一些背景图进行数字化,但是在国内,目前只有分辨率非常低Yahoo卫星地图可以用。至于Google的卫星地图,由于License的原因,你是不能在编辑自由地图时使用的。

上海的OSM已经非常完善,数据完善的程度、渲染出地图的壮观已经让你难以想象,连去年开通的张江电车已经在地图上标出,还有各个站名。注册后可以发现,仅仅在浦东就有十几位OSM的贡献者。相比之下,南京在这方面一片空白,甚至还没有苏州的地图完善。考虑到南京有这么多学校这么多GIS专业学生,每年做那么多无用的、自以为是的数字化作业,真正有意义的工作却无人问津,并非没有人愿意去做,而恐怕是大部分人对OSM都闻所未闻。

今后每周我回抽出一定的时间来编辑南京的地图,也算是为家乡做一些贡献吧。

The post is brought to you by lekhonee v0.7

Firefox addon for wms development

03月 12, 2010

今天一个叫做WMS Inspector的Firefox扩展发布了。这个扩展用来查看页面上的WMS请求,还可以生成GetCapabilities的Report。

Screenshot
Screenshot-GetCapabilities request
Screenshot-Capabilities report - WMS Inspector - Mozilla Firefox

New features in JTS 1.11

03月 3, 2010

JTS最近发布了1.11版本,新增了:

计算Delaunay三角网和Voronoi多边形:
[cc lang="java"]
import java.util.ArrayList;
import java.util.Collection;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.triangulate.DelaunayTriangulationBuilder;
import com.vividsolutions.jts.triangulate.VoronoiDiagramBuilder;

/**
*
* @author Sun Ning/SNDA
* @since 2010-3-3
*/
public class DelaunayAndVoronoiApp {

/**
* create some predefined sites
* @return
*/
public static Collection getPredefinedSites(){
double[][] coords = {{100,27},{28, 50},{29, 40},{32, 90}, {12, 26}};
ArrayList coordinates = new ArrayList(coords.length);

for(int i=0; i<coords.length; i++){
coordinates.add(new Coordinate(coords[i][0], coords[i][1]));
}

return coordinates;
}

/**
*
* @param coords
* @return a geometry collection of triangulations
*/
public static Geometry buildDelaunayTriangulation(Collection coords){
DelaunayTriangulationBuilder builder = new DelaunayTriangulationBuilder();
builder.setSites(coords);
return builder.getTriangles(new GeometryFactory());
}
/**
*
* @param coords
* @return a collection of polygons
*/
public static Geometry buildVoronoiDiagram(Collection coords){
VoronoiDiagramBuilder builder = new VoronoiDiagramBuilder();
builder.setSites(coords);
return builder.getDiagram(new GeometryFactory());
}

/**
*
* @param args
*/
public static void main(String[] args){
Collection coordinates = getPredefinedSites();

/**
* Delauny
*/
GeometryCollection triangulations
= (GeometryCollection)buildDelaunayTriangulation(coordinates);

int total = triangulations.getNumGeometries();
System.out.printf(“Total triangulations: %d\n”, total);
for(int i=0; i<total; i++){
Geometry g = triangulations.getGeometryN(i);
Coordinate[] coords = g.getCoordinates();
System.out.printf("Triangulation %d: ", i);
for(Coordinate c : coords){
System.out.printf("(%.3f, %.3f) ", c.x, c.y);
}
System.out.println();
}

/**
* Voronoi
*/
GeometryCollection diagram = (GeometryCollection)
buildVoronoiDiagram(coordinates);
int totalDia = diagram.getNumGeometries();
for(int i=0; i<totalDia; i++){
Geometry g = diagram.getGeometryN(i);
Coordinate[] coords = g.getCoordinates();
System.out.printf("Diagram %d: ", i);
for(Coordinate c : coords){
System.out.printf("(%.3f, %.3f) ", c.x, c.y);
}
System.out.println();
}
}

}

[/cc]
输出:
[cc lang="text"]
Total triangulations: 5
Triangulation 0: (32.000, 90.000) (12.000, 26.000) (28.000, 50.000) (32.000, 90.000)
Triangulation 1: (32.000, 90.000) (28.000, 50.000) (100.000, 27.000) (32.000, 90.000)
Triangulation 2: (100.000, 27.000) (28.000, 50.000) (29.000, 40.000) (100.000, 27.000)
Triangulation 3: (100.000, 27.000) (29.000, 40.000) (12.000, 26.000) (100.000, 27.000)
Triangulation 4: (12.000, 26.000) (29.000, 40.000) (28.000, 50.000) (12.000, 26.000)
Diagram 0: (-76.000, 88.625) (-76.000, 178.000) (176.713, 178.000) (72.699, 65.730) (-38.235, 76.824) (-76.000, 88.625)
Diagram 1: (-76.000, -62.000) (-76.000, 88.625) (-38.235, 76.824) (11.978, 43.348) (56.422, -10.619) (57.006, -62.000) (-76.000, -62.000)
Diagram 2: (11.978, 43.348) (-38.235, 76.824) (72.699, 65.730) (67.316, 48.882) (11.978, 43.348)
Diagram 3: (176.713, 178.000) (188.000, 178.000) (188.000, -62.000) (57.006, -62.000) (56.422, -10.619) (67.316, 48.882) (72.699, 65.730) (176.713, 178.000)
Diagram 4: (11.978, 43.348) (67.316, 48.882) (56.422, -10.619) (11.978, 43.348)
[/cc]

将Geometry对象转换成Shape对象,绘制在JPanel上:
[cc lang="java"]
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.util.Collection;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.vividsolutions.jts.awt.ShapeWriter;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import java.util.Random;

/**
*
* @author Sun Ning/SNDA
* @since 2010-3-3
*/
public class JTS2Awt {

public static void showUI(final Shape… shape){
JFrame jframe = new JFrame("JTS Geometry to AWT Shape");

JPanel jp = new JPanel(){
@Override
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
if(shape != null){
for(Shape s: shape){
g2d.setColor(new Color(Color.HSBtoRGB(new Random().nextFloat(), 1f, 0.6f)));
g2d.draw(s);
}
}
}
};
jp.setPreferredSize(new Dimension(150, 150));

jframe.getContentPane().add(jp);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.pack();
jframe.setVisible(true);
}

public static Shape toShape(Geometry geom){
ShapeWriter writer = new ShapeWriter();
return writer.toShape(geom);
}

public static void main(String[] args){
Collection coords = DelaunayAndVoronoiApp.getPredefinedSites();
Geometry geomT = DelaunayAndVoronoiApp.buildDelaunayTriangulation(coords);
Geometry geomD = DelaunayAndVoronoiApp.buildVoronoiDiagram(coords);

showUI(toShape(geomT), toShape(geomD));
}

}

[/cc]
jts

Get your conky location aware

02月 19, 2010

2010-02-19-225826_188x44_scrot

Add this in your conkyrc
[cc lang="text" nowrap="0"]
${exec curl -s “http://api.hostip.info” | xpath -e “//gml:featureMember/Hostip/gml:name/text()” -q} ${exec curl -s “http://api.hostip.info” | xpath -e “//gml:featureMember/Hostip//gml:coordinates/text()” -q}
[/cc]

Hostip is well known as a service provider of the geoclue framework. It translates IP address to geolocation information. The API we use will return a GML document like
[cc lang="xml" nowrap="0"]

This is the Hostip Lookup Service
hostip

inapplicable

58.212.88.212
Nanjing
CHINA
CN

118.883,32.05

[/cc]

Because it uses ip to lookup your address, you cannot expect higher resolution and precision currently.

Emerillon: map viewer for gnome desktop

01月 30, 2010

自从libchamplain / geoclue等库发布之后,gnome桌面的地理信息工具和支持发展很迅速:例如之前提到过的eye-of-gnome的地理信息插件,根据EXIF信息在地图上显示。现在gnome桌面上终于有一个专门的地图查看器了,仍然是基于libchamplain,名字叫做emerillon

Emerillon Map Viewer

仍然是使用open street maps,这两年上海的地图发展的非常不错,连最新的二号线东延都已经被标注出来了。相比之下,南京的地图就还是一片空白。

在Ubuntu上安装emerillon,可以从其网站上下载源码编译安装:
http://www.novopia.com/emerillon/download.html

emerillon的几个主要依赖:

  • libchamplain
  • librest
  • ethos

libchamplain在ubuntu 9.10的仓库已经包含

librest也在软件仓库中,不过需要注意的是ubuntu将librest安装在pkg-config里时的名字叫做rest.pc,而emerillon查找的是rest-0.6.pc,所以需要手动建立一个软连接:
sudo ln -s /usr/lib/pkgconfig/rest.pc /usr/lib/pkgconfig/rest-0.6.pc

ethos是一个Gtk的插件框架,目前还不在软件仓库中,需要从网站下载代码编译:
http://git.dronelabs.com/ethos/
ethos网站上提到的PPA源中的版本偏旧,不建议使用。

实际上也可以直接添加emerillon的PPA源:
deb http://ppa.launchpad.net/mathieu-tl/emerillon/ubuntu karmic main
deb-src http://ppa.launchpad.net/mathieu-tl/emerillon/ubuntu karmic main

另外,也可以通过Ubuntu Tweak安装。

作者Blog:
http://blog.pierlux.com/en/

Adding Ubuntu-GIS repository

11月 29, 2009

在Ubuntu上安装GIS软件,可以添加这个PPA源:
deb http://ppa.launchpad.net/ubuntugis/ubuntugis-unstable/ubuntu karmic main

导入GPG KEY
gpg –keyserver subkeys.pgp.net –recv 089EBE08314DF160
gpg –export –armor 089EBE08314DF160 | sudo apt-key add -

这个源中目前包含了最新版本的GIS软件:

  • gdal 1.6
  • grass
  • qgis

Mapping Geometry in Grails and MySQL

10月 6, 2009

针对地理数据的ORM,有一个Hibernate的扩展HibernateSpatial项目可以将JTS对象映射到MySQL/PostGIS/Oracle中。这个扩展同样可以用在Grails里,这里有一篇简单的介绍,关于在Grails和MySQL中管理地理数据:
http://www.grails.org/MySQL+GIS-Geometry+with+Grails

不过按照这个文章里介绍的方法用,很可能会遭遇这样的报错:

org.hibernate.MappingException: No Dialect mapping for JDBC type: 2003

这个问题最终在这里得到了解答:
http://n2.nabble.com/No-Dialect-mapping-for-JDBC-type-2003-td1141106.html
按照邮件列表里的反映,上面的配置在Postgis里是可以work的,但是如果用Mysql还需要指定JPA的columnDefinition,对应的Hibernate属性是sql-type。虽然作者承诺会在今后的版本里修改这个问题,不过眼下的M2版本还没有修正这个问题。为此,Grails的用户特地提出在Grails中加入sql-type的支持:
http://jira.codehaus.org/browse/GRAILS-3201
现在按照下面文档的说明,可以在mapping里指定sqlType了:
http://grails.org/doc/latest/ref/Database%20Mapping/column.html

实例代码里的domain定义应该改成:

import com.vividsolutions.jts.geom.Polygon
import org.hibernatespatial.GeometryUserType

public class MyPoly {
    String name
    Polygon poly

    static mapping = {
        poly type: GeometryUserType, sqlType:"GEOMETRY"
    }

}

于是,再也没有莫名其妙的No Dialect报错了。

Reading GPS Data From EXIF Using Groovy

10月 5, 2009

废话不说了,直接上code吧:

import com.drew.imaging.jpeg.*;
import com.drew.metadata.*;
import com.drew.metadata.exif.*;

file = new File("sample.jpg");
meta = JpegMetadataReader.readMetadata(file);

gpsdir = meta.getDirectory(GpsDirectory.class);
lat = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
lon = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
lats = lat[0].doubleValue() + lat[1].doubleValue()/60 + lat[2].doubleValue()/3600;
lons = lon[0].doubleValue() + lon[1].doubleValue()/60 + lon[2].doubleValue()/3600;
println(lats);
println(lons);

metadata-extractor似乎是现在惟一的读取exif信息的java库。在ivy中可以添加这样一个依赖:

<dependency org="com.drewnoakes" name="metadata-extractor" rev="2.4.0-beta-1" conf="runtime"/>

libchamplain: Map Widget of Gnome Desktop

10月 3, 2009

续昨,虽然昨天升级到了gnome2.28,也安装了libchamplain,但是却没有发现这个库应用。Ubuntu上的Empathy似乎是没有把location support编译进去。看不到效果怎么办,好在libchamplain的作者还提供了另一个应用。这是一个eog的插件,用于读取照片的exif信息中的gps latitude和longitude,进而将他显示在libchamplain的地图里。

安装这个插件可以follow官方网站上的做法,当然开发包、头文件之类的东西要齐备。

libchamplain使用了clutter,在载入地图的时候还有fadein的效果。clutter是将要应用在下一代桌面gnome shell中的图形特效库。

插件是用C写的,可以看成是libchamplain的一个例子,作者说已经有了其他语言的binding,如此未来在gnome桌面上开发tile地图程序会变得简单。

GeoHash

09月 19, 2009

今天偶然看到一个Ruby的GeoHash库,功能非常简单,就是将经纬度坐标哈希成字符串,并且,利用这个字符串可以粗略地比较两点的距离。这个库的代码host在GitHub上,要安装,需要将github加入gem:
sudo gem sources -a http://gems.github.com
sudo gem install davotroy-geohash

用法,irb:
irb(main):001:0> require ‘rubygems’
=> true
irb(main):002:0> require ‘geohash’
=> true
irb(main):003:0> GeoHash.encode(32.168, 118.54)
=> “wtsr12n0nj”
irb(main):004:0> GeoHash.decode(‘wtsr12n0nj’)
=> [32.168, 118.54]

代码可以从github上获得
git clone git://github.com/davetroy/geohash.git

GeoHash的核心部分用C实现,仔细看一下可以发现是一个四叉树的结构:

[codesyntax lang="c"]
#define BASE32	"0123456789bcdefghjkmnpqrstuvwxyz"
static void encode_geohash(double latitude, double longitude, int precision, char *geohash) {
	int is_even=1, i=0;
	double lat[2], lon[2], mid;
	char bits[] = {16,8,4,2,1};
	int bit=0, ch=0;

	lat[0] = -90.0;  lat[1] = 90.0;
	lon[0] = -180.0; lon[1] = 180.0;

	while (i < precision) {
		if (is_even) {
 			mid = (lon[0] + lon[1]) / 2;
 			if (longitude > mid) {
				ch |= bits[bit];
				lon[0] = mid;
			} else
				lon[1] = mid;
		} else {
			mid = (lat[0] + lat[1]) / 2;
			if (latitude > mid) {
				ch |= bits[bit];
				lat[0] = mid;
			} else
				lat[1] = mid;
		}

		is_even = !is_even;
		if (bit < 4)
			bit++;
		else {
			geohash[i++] = BASE32[ch];
			bit = 0;
			ch = 0;
		}
	}
	geohash[i] = 0;
}

[/codesyntax]

可惜在学校的时候没有看到这么精致的代码。


加关注

Get every new post delivered to your Inbox.