Perfomed my first commit to OpenStreetMap.org

每每看到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

New features in JTS 1.11

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

计算Delaunay三角网和Voronoi多边形:

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<coordinate> getPredefinedSites(){
        double[][] coords = {{100,27},{28, 50},{29, 40},{32, 90}, {12, 26}};
        ArrayList<coordinate> coordinates = new ArrayList<coordinate>(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<coordinate> 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<coordinate> coords){
        VoronoiDiagramBuilder builder = new VoronoiDiagramBuilder();
        builder.setSites(coords);
        return builder.getDiagram(new GeometryFactory());
    }

    /**
     *
     * @param args
     */

    public static void main(String[] args){
        Collection<coordinate> 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();
        }
    }

}

输出:

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)

将Geometry对象转换成Shape对象,绘制在JPanel上:

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<coordinate> coords = DelaunayAndVoronoiApp.getPredefinedSites();
        Geometry geomT = DelaunayAndVoronoiApp.buildDelaunayTriangulation(coords);
        Geometry geomD = DelaunayAndVoronoiApp.buildVoronoiDiagram(coords);

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

}

jts

Get your conky location aware

2010-02-19-225826_188x44_scrot

Add this in your conkyrc

${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}

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

<?xml version="1.0" encoding="ISO-8859-1" ?>
<hostipLookupResultSet version="1.0.1" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.hostip.info/api/hostip-1.0.1.xsd">
 <gml:description>This is the Hostip Lookup Service</gml:description>
 <gml:name>hostip</gml:name>
 <gml:boundedBy>
  <gml:Null>inapplicable</gml:Null>
 </gml:boundedBy>
 <gml:featureMember>
  <hostip>
   <ip>58.212.88.212</ip>
   <gml:name>Nanjing</gml:name>
   <countryName>CHINA</countryName>
   <countryAbbrev>CN</countryAbbrev>
   <!-- Co-ordinates are available as lng,lat -->
   <ipLocation>
    <gml:pointProperty>
     <gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
      <gml:coordinates>118.883,32.05</gml:coordinates>
     </gml:Point>
    </gml:pointProperty>
   </ipLocation>
  </hostip>
 </gml:featureMember>
</hostipLookupResultSet>

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

Emerillon: map viewer for gnome desktop

自从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/