The distribution of intelligence

This could be a big title for the content.

I just work out a heat map based on the data of users who enrolled online courses offered by Stanford.


(Click to enlarge)

I think you already have your ideas about this map. New England, California and Central Europe has higher density than any other area of the world. Also you can find some light in South America, East Coast Australia, India and China. I think this could be an overview of how intelligence distributed around the globe.

GPS数据采集与OpenStreetMap编辑

简单介绍一下通过GPS采集道路数据并上传到OpenStreetMap的流程。

采集

首先你需要一个GPS数据记录器,或者叫做GPS Logger。这类产品在淘宝上可以找到很多,台湾的长天(HOLUX)是相对价廉物美的品牌。我的设备是HOLUX m1000c,下文以此为例。

m1000c的使用非常简单。开机之后,指示GPS的黄灯点亮,设备开始搜索GPS卫星。根据你所出的位置、遮挡情况以及天气情况,搜星的时间略有不同。当GPS黄灯开始闪烁时Logger即开始记录数据,默认情况下m1000c每隔5秒记录一次数据。每次重启后,Logger都会新开一条记录。

导入

对Windows用户,将Logger通过USB线与电脑连接后,可以利用随机附带的软件将数据导入为GPX格式。对Linux用户,可以利用gpsbabel导入数据:
gpsbabel -t -i m241 -f /dev/ttyACM0 -o gpx -F myfile.gpx

其中:

  • -t 表示数据为track类型
  • -i m241 输入格式为HOLUX m241,m1000c使用的是这种格式
  • -f /dev/ttyACM0 输入设备是/dev/ttyACM0
  • -o gpx 输出格式是gpx
  • -F 输出文件myfile.gpx

在一些发行版上,需要root权限访问/dev/ttyACM0,所以不要忘记将输出的文件chown给普通用户。

导入完成后可以利用下面的命令清空Logger
gpsbabel -i m241,erase_only=1 -f /dev/ttyACM0

在Windows和Linux上都可以使用Viking查看导入的数据:

  • 启动viking
  • 添加一个OSM地图图层: Layers->New Map Layer
  • 导入GPX数据: File->Append File…
  • 下载OSM地图,右键点击左侧的Map图层,Download Missing Onscreen Maps

Screenshot at 2011-10-05 09:45:20

编辑

Windows和Linux用户都可以通过Merkaartor编辑OSM数据。

启动Merkaartor,导入GPS数据: File->Import。
选择左侧的GPS图层,对需要导入的数据,右键点击Extract Drawing Layer将GPS转换为可编辑数据。
Screenshot at 2011-10-05 10:57:17

根据实际情况编辑道路数据的属性。对GPS转换的数据,选择菜单Feature->Force Upload,将其加入dirty set准备上传。点击Upload即可将数据上传到OpenStreetMap。

Using Google closure library with ClojureScript

Google closure library is shipped with ClojureScript, and could be compiled with ClojureScript into a minimized javascript file. So closure library is doubtlessly the first candidate when you are considering to use an external Javascript library in your cljs browser application.

However, different from clojure’s interoperability with Java, ClojureScript has its own characteristics when you are interoperating with JavaScript and JavaScript based libraries.

Clojure types are not fully compatible with JavaScript types
In ClojureScript, you can never treat a Clojure map as a JavaScript object although they have similar characteristics. You have to do some conversion before passing a clojure map to javascript functions. Matthew Gilliard made a sample of such conversion.

JavaScript package is not Clojure namespace
This could be a common mistake for ClojureScript newbie. Actually, JavaScript doesn’t have concept of “Package” or “Namespace”. Many JavaScript libraries(dojo, Google Closure) made enhancement on this. ClojureScript also takes advantage of this mechanism. So before you start to coding with closure, you may browse closure library API document, and find a module called goog.net which includes lots of types. Then you write this:

(ns myjs
  (:require [goog.net :as gnet]))

But compiler shows you “ERROR: JSC_MISSING_PROVIDE_ERROR. required “goog.net” namespace never
provided at … “. This is not a PATH issue. The root cause is that closure module has a lower granularity than Clojure ones. Types are often contained in their own modules. You can find closure source code in clojurescript/closure/library/closure. Modules are declare with goog.provide function. Thus, you should require this name instead of the logical module name.

(ns myjs
  (:require [goog.net.XhrIo :as gxhr]))

In addition, ClojureScript does not support ‘use’.

Just use full name for JavaScript class
For functions contains in some module, you can refer it with the clojure way:

(ns myjs
  (:require [goog.dom :as dom]))
(dom/$ "element-id")

But for classes, just use the full name and ignore the module alias.

(ns myjs
  (:require [goog.net.XhrIo :as gxhr]))
(def xhr (goog.net.XhrIo.))

These are basic tips before you start using Closure with ClojureScript. Leveraging on Google’s closure library, you can create cross-browser JavaScript application with Clojure easily.

ClojureScript Recipes

一周前左右有人说javascript是assembly language for the web, 结果一周不到clojurescript发布了。闹了半天clojure 1.3迟迟不发布是因为effort都迫不及待地转移到向javascript迁移上去了。简单地说cljs是clojure在javascript上的实现,通过cljsc可以把clojure编译成js,运行在浏览器里或是Node环境里。

目前clojurescript还没有正式的发布版本,需要从github签出开发版本。

  1. cljs是在Oracle JDK上开发的,引用了sun.org.mozilla.javascript.internal.Context,这个类在OpenJDK里叫做sun.org.mozilla.javascript.Context。所以没有办法,暂时只能在Oracle JDK上用clojurescript。
  2. cljs自带的所有脚本,启动jvm时,heap size都是开2G的。可怜我所有的内存才2.5G,还是分布在两台电脑上(#@&*……@¥@!)。不过我手动把它改为512M后cljsc还是依然可以正常运行的。
  3. cljs与javascript的互操作是最麻烦的部分,一般情况cljs通过(js*)这个form来访问javascript数据和对象,比如访问document:(js* “document”)。进而访问getElementById时,即(.getElementById (js* “document”) “some-id”)。
  4. cljs访问js对象时,通过(aget)而不是(.属性名),例如(aget rage “ups”)编译后为rage.ups,如果(.ups rage)就会被当作函数调用。不过奇怪的是如果(.title rage)依然会被编译成rage.title。这种不一致的情况在对.url也存在。当然用aget是可以获得一致的结果的(aget在clojure里是根据索引取java数组的form,在cljs里可以支持js object了)。
  5. 当访问无参数的js对象时,cljs与clojure有不同。例如在clojure里(.toString date),而在cljs里,这样写编译的结果是date.toString,注意没有括号,直接访问这个function对象了。所以在cljs里,正确的写法有些变化(. date (toString))。
  6. 创建js对象,可以通过(js-obj)这个form
  7. 修改dom属性,需要用(set!)这个form,如 (set! (.src img) “http://xxx”)。如果把cljs用在网页里,类似这样的操作比比皆是,这和clojure大不相同。
  8. cljs里没有STM,暂时也不支持ref。可以直接用def定义变量,随意地访问和修改,js环境是单线程环境。
  9. 对于要对外访问的方法,在声明时加上^:export可以让编译器不修改方法的名字。

用cljs开发有趣归有趣,调试还是很困难的,建议开发的时候就写好打log的代码(js* “console.log”),因为目前cljsc编译的速度也不快,反复地修改代价还是挺高的。当然,为了cool,以上都不是问题。

使用defrecord与defprotocol的注意事项

简单地说,protocol是clojure中的接口,record是clojure中的数据类型。

可以通过这样的code定义一个protocol

(defprotoco DummyProtocol
  "doc string..."
  (method-one [self x] "doc string..."))

需要注意的是,protocol里所有方法的第一个参数都是self/this参数(类似python),从第二个开始才是调用时传入的参数。如果方法要重载呢?

(defprotocol DummyProtocol
  "doc string..."
  (method-one [self x] [self x y] "doc string")
)

Apress的 Practical Clojure 书里的例子,给重载的参数表加上了括号,这样会导致编译错误(注记)。

定义一个record实现protocol

(defrecord DummyRecord [a b c]
  DummyProtocol
  (method-one [self x] (+ a x))
  (method-one [self x y] (+ a x y)))

Practical Clojure里关于这部分的代码,又丢掉了self参数(注记)。

最后还有一个问题,如果直接use你的ns,你会发现调用record时出现:
java.lang.IllegalArgumentException: Unable to resolve classname: DummyRecord

怎么回事,不是都use了吗?原因是record被编译成了java对象,所以引用时要用java对象的引用方式,import之。