Wednesday, April 14, 2010

Several approaches to do Geolocation by IP address programmatically

Geolocation is the process of identifying the real-world geographic location based on a device’s IP address (either connected through cable or WiFi access point), GSM/CDMA cells IDs or GPS and so on. The result of Geolocation is essentially a pair of coordinate (lat and lon) on earth, and if combined with other service like Geocoding the result can also be translated to a well-known place name or a physical address.
Geolocation by IP address although  may not be as accurate as GPS sometimes, does have great value in many of today’s location aware web applications. One of the typical examples is that when you do search on internet the search engine will detect the location by your IP address and automatically filter and sort the results based on that information. In this article I will be looking at some existing approaches I’ve played with to do Geolocation by IP address.

1. W3C Geolocation API
If you’re in JavaScript environment, W3C Geolocation API is definitely the approach you should follow because it is the standard. Independent of the actual source of the location information which is not just limited to IP address but can also be WiFi and Bluetooth MAC address, GSM/CDMA cell IDs, GPS or so, it actually defines a high-level interface to location associated information with devices hosting the implementation. Other than that W3C Geolocation API also defines the way you request repeated position updates through callback mechanism as well as requesting an aged position.
Mozilla Firefox is a very good example of such implementation. The code example below illustrate the common work flow to get your current location in a JavaScript application:
if(navigator.geolocation) {  
/* geolocation is available */  
navigator.geolocation.getCurrentPosition(
function(position) { // success callback  
//alert(position.coords.latitude + "," + position.coords.longitude);
alert(position.address.city);                
},
function() { // failure callback
// error handling
},
{
// other options
timeout:30000
}
);    
} else {      
// browser does not support Geolocation API
} 
In Firefox’s implementation, it gives you not only the lat and lon value of your location but also gives you the address associated with that coordinate. Under the hood Firefox takes advantage of Google Location Service which I believe provided all necessary infrastructure.

2. Google Geolocation API in Gears
Google Gears Geolocation API implements the same W3c Geolocation interfaces but in a more device independent, location information source independent, and browser independent way. It’s online doc is very good and concise for people who would like to use it in their web application.

3. Google Location Service
Google Location Service is far more than an interfaces that only provides geolocation function, but it’s totally valid to use that as a Geolocation service. Without even realizing, it is definitely behind a lot of its own services like Search and Latitude which sometimes you don’t even realize. It is also backing up many other Geolocation API implementations like Firefox. But unfortunately I was able to find out an explicit native Geolocation service API either in JavaScript or in REST flavor. So it’s probably impossible to directly communication with Google Location Service by feeding an IP address and getting a coordinate back.


4. Other Geolocation by IP service providers
HostIP, mentioned in Andrew Turner’s Introduction to Neogeography, HostIP has the largest free database and service for GeoIP (the term Andrew uses for geolocation by IP address). The best part of HostIP is that it freely distribute it’s database with regular updates. Since I don’t find any information regarding license and term of use for HostIP database I would assume it is in public domain. Programmatically HostIP provides a simple and lightweight RESTful API for people to locate themselves by IP. It doesn’t do much but it’s good at what it does. Because of its REST nature, it is not limited to any program environment except that you might need a proxy in your JavaScript web application. Below is an example:
http://api.hostip.info/country.php
US
http://api.hostip.info/get_html.php?ip=12.215.42.19
Country: UNITED STATES (US)
City: Sugar Grove, IL
IP: 12.215.42.19
http://api.hostip.info/get_html.php?ip=12.215.42.19&position=true
Country: UNITED STATES (US)
City: Sugar Grove, IL
Latitude: 41.7696
Longitude: -88.4588
IP: 12.215.42.19
http://api.hostip.info/?ip=12.215.42.19
[use the URL above for an example - XML too long to paste below]
Finally its website itself provides a simple browser based GUI for people who don’t want to bother coding their own app.

GeoIP: MaxMind has a free GeoIP database and API called GeoLite City. MaxMind and it GeoIP product suite does not have a publicly hosted RESTful service or API for directly use but it allows you to download their database in either binary format or CSV format (mainly for you to load data into SQL database) to host locally. The API for accessing the local database is available in many platforms (C, C#, MS COM, PHP, Java, Perl Python, Ruby etc.). The workflow is to first download the database to local disk and uncompress it, then use any of the API available the query the database. Below is an example I tried out using its Java API:
public void lookupServiceTest() {  
try {
LookupService cl = new LookupService("E:\\projects\\geoip\\geolite-city-database\\GeoLiteCity.dat", LookupService.GEOIP_MEMORY_CACHE);
Location location = cl.getLocation("xxx.xxx.xxx.xxx");
System.out.println(" countryCode: " + location.countryCode +
"\n countryName: " + location.countryName +
"\n region: " + location.region +
"\n regionName: " + regionName.regionNameByCode(location.countryCode, location.region) +
"\n city: " + location.city +
"\n postalCode: " + location.postalCode +
"\n latitude: " + location.latitude +
"\n longitude: " + location.longitude +
"\n metro code: " + location.metro_code +
"\n area code: " + location.area_code +
"\n timezone: " + timeZone.timeZoneByCountryAndRegion(location.countryCode, location.region));
cl.close();
} catch (IOException e) {
System.out.println("IO Exception");
}  
}
GeoIP API is free and open source under GPL/LGPL license, and the GeoLite City database itself is also free under its database license. MaxMind also provides an advanced commercial version called GeoIP City, which is functionally equal to the free one but has more accuracy, different redistribution license and more frequent updates.

Sunday, March 28, 2010

Using Java Map Projection Library in Android

Recently I was writing a little mapping application on Android, which basically takes Open Street Map tiles as base layer, and also has the ability to overlay some vector features on top. In this application there is need to convert coordinates back and forth between WGS84 and Mercator projection, so I need certain library to do the job. Given the popularity of PROJ.4, the pure Java port of it, Java Map Projection Library (name it “PROJ4Java” below), seems to be an obvious choice so I spend some hours during the weekend to see how it works.
First you can get the latest version (1.0.6) of PROJ4Java from this link. The size of the library is very small and lightweight with only 1.17MB for complete source code and 247KB for compiled jar file. If you’re familiar with PROJ.4 itself then this Java version should be quite straight forward, otherwise there isn’t much sample code or tutorial on its homepage. But the basic workflow is quite simple as described by its project homepage:
To use the library, you need to create a projection, either directly by calling its constructor or via one of the factory methods. Once you have a Projection, you can use it to convert between latitude/longitude and projected map units (by default, metres) by calling the transform and transformInverse methods.
A major issue of using PROJ4Java on Android is that out of box it can’t be run due to the fact that it is using some classes in java.awt.geom.* which is not available in Android SDK. But since the only major AWT class used by PROJ4Java is Point2D, it’s not difficult at all to work around by replacing it with your own 2D point class. In my case I use the point class from JTS library. After taking out all Java awt class references from source code and replacing them with JTS class, everything works pretty well on Android.
Another thing I modifies on the source code is to get rid of those projection implementations I don’t need. Mercator projection is the only one I need so far so that I am able to reduce the size of compile jar file to only 168KB. Here is a list of classes I extracted from original source code to support only Mercator projection:
    • com.jhlabs.map.*
    • com.jhlabs.map.proj.CylindricalProjection
    • com.jhlabs.map.proj.Ellipsoid
    • com.jhlabs.map.proj.MercatorProjection
    • com.jhlabs.map.proj.Projection
    • com.jhlabs.map.proj.ProjectionFactory
    • com.jhlabs.map.proj.ProjectionException
    • com.jhlabs.map.proj.Ellipsoid
ProjectionFactory class is the entry point for you to use PROJ4Java, which provides several static methods to create an instance of Projection. Below are some use cases I figured out by briefly reading through the source code:
(1) ProjectionFactory.getNamedPROJ4CoordinateSystem(String name) takes a well-known projection code like “epsg:4326” and returns a Projection:
1: String name = "epsg:3785";    
2: Projection proj = ProjectionFactory.getNamedPROJ4CoordinateSystem(name);
(2) ProjectionFactory.fromPROJ4Specification(String[] params) takes a set of PROJ.4 parameters and returns a Projection:
1: String[] params = {
2:     "proj=tmerc",
3:     "lat_0=37.5",
4:     "lon_0=-85.66666666666667",
5:     "k=0.999966667",
6:     "x_0=99999.99989839978",
7:     "y_0=249999.9998983998",
8:     "ellps=GRS80",
9:     "datum=NAD83",
10:     "to_meter=0.3048006096012192",
11:     "no_defs"
12: };
13: ProjectionFactory.fromPROJ4Specification(params);
(3) Create an instance of customized projection. All supported projection definition strings (PROJ.4 parameters for ) for PROJ4Java are listed in text files under folder “nad” that is coming with the library. These files are “epsg”, “esri”, “nad27”, “nad83”, and “world”, and a sample projection definition string is like below:
1: <2965> +proj=tmerc +lat_0=37.5 +lon_0=-85.66666666666667 +k=0.999966667 +x_0=99999.99989839978 +y_0=249999.9998983998 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs
2: 
To add a new customized projection just create a definition string (usually modify certain parameter values upon an existing projection) and add it into one of those files, or create a new text file under “nad” folder with the definition string, then use the code below:
1: Projection proj = null;
2: try {
3:     // assume that "epsg:900913" projection defintion 
4:     //   has been added in text file "others"
5:     proj = ProjectionFactory.readProjectionFile("others", "900913");    
6: } catch(IOException e) {
7:     e.printStackTrace();
8: }
(4) Transform between lat/lon and projection units with a Projection instance:
1: Projection epsg3785 = ProjectionFactory.getNamedPROJ4CoordinateSystem("epsg:3785");    
2:               
3: System.out.println("transform from latlon to epsg:3785");
4: System.out.println("latlon: -117.5931084, 34.1063989");
5: Point pEpsg3785 = epsg3785.transform(-117.5931084, 34.1063989);
6: System.out.println("epsg:3785: " + pEpsg3785.getX() + ", " + pEpsg3785.getY());
7:     
8: System.out.println("transform from epsg:3785 to latlon");
9: System.out.println("epsg:3785: " + pEpsg3785.getX() + ", " + pEpsg3785.getY());
10: Point latlon = epsg3785.inverseTransform(pEpsg3785);
11: System.out.println("latlon: " + latlon.getX() + ", " + latlon.getY());
12: 
13:   

Tuesday, March 9, 2010

WMS TIME support in MapServer

In context of my last blog, I’m looking at the WMS TIME support in OSGeo MapServer. MapServer has a very concise and helpful doc on the scope and usage of WMS TIME in its implementation. It is always a good idea to briefly go through the documentation first before you spend a lot time trying something that may not be even  implemented.
My test data (in shapefile format) is very simple such that the picture below tells all about it:
0
Each point feature has a field “TIME” which has time value in syntax “YYYY-MM-DD HH:MM:SS”. This is valid ISO8601 time format and supported by MapServer. All points have a time stamp within day 2010-02-16. To enable time on WMS in MapServer is straight forward given that you have some experience of publishing shapefile as a WMS in MapServer. The only extra thing you need to configure is to specify time field, time extent and default time value in map file. That’s it and MapServer documentation is very clear on how to do that.
1: LAYER
2:     NAME 'points'
3:     METADATA
4:         "ows_title" "points"
5:         "gml_include_items"   "all"
6:         "wms_include_items"   "all"
7:         # TIME Related
8:         "wms_title"          "points"
9:         "wms_timeextent"     "2010-02-16T00:00:00Z/2010-02-17T00:00:00Z/P1H"
10:         "wms_timeitem"       "TIME"
11:         "wms_timedefault"     "2010-02-16T12:00:00Z,2010-02-16T18:00:00Z"
12:     END
13: ...
14: END
After saving the map file and refresh the GetCapabilities response you will notice information of time dimension is advertised.
1: <Dimension name="time" units="ISO8601" default="2010-02-16T12:00:00Z,2010-02-16T18:00:00Z" nearestValue="0">
2:     2010-02-16T00:00:00Z/2010-02-17T00:00:00Z/P1H
3: </Dimension>
4: 
The time extent value in <Dimension> is whatever you specified in “wms_timeextent” so it doesn’t smart enough to calculate the extent from data, and neither does MapServer do any validation on the time extent string. A trick I did in map file is to provide a special default time value “2010-02-16T12:00:00Z, 2010-02-16T18:00:00Z,”, which will inform me if certain time string is not correctly processed by MapServer thus default value is used.

Now it’s time to try a few requests against this time enabled WMS. According to doc the supported request syntax for time are “t”, “t1,t2…”, “t1/t2”, and “t1/t2,t3/t4…” and “t” should be any valid ISO8601 time string. I skipped the time zone because there is nowhere indicating it is implemented (you can not configure time zone at server-side thus it makes no sense to request from client). Below is a list of different time values that have been tried:
  • …&TIME=2010 (not recognized, default time value is returned)
  • …&TIME=2010-02 (not recognized, default time value is returned)
  • …&TIME=2010-02-16 (recognized, only point of “2010-02-16 00:00:00” returned)
  • …&TIME=2010-02-16T12 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T12:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T12:00:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T14:59:59 (recognized, only point of “2010-02-16 14:59:59” returned)
  • …&TIME=2010-02-16T15:00:01 (recognized, only point of “2010-02-16 15:00:01” returned)
  • …&TIME=2010-02-16T15:00:00.000 (recognized, only point of “2010-02-16 15:00:00” returned)
  • …&TIME=2009/2010 (not recognized, default time value is returned)
  • …&TIME=2010/2011 (not recognized, default time value is returned)
  • …&TIME=2010-02/2010-03 (not recognized, default time value is returned)
  • …&TIME=2010-02-16/2010-02-17 (recognized, all points returned)
The test results above actually proves the time interpretation described in the documentation:
When MapServer receives a request with a TIME parameter, it transforms the time requests into valid expressions that are assigned to the filter parameter on layers that are time-aware.
Here are some examples of how different types of requests are treated (wms_timeitem is defined here as being “time_field”):
single value (2004-10-12) transforms to (`[time_field]` eq `2004-10-12`)
multiple values (2004-10-12, 2004-10-13) transform to (`[time_field]` eq `2004-10-12` OR `[time_field]` eq `2004-10-13`)
single range : 2004-10-12/2004-10-13 transforms to ((`[time_field]` ge `2004-10-12`) AND (`[time_field]` le `2004-10-13`))
multiple ranges (2004-10-12/2004-10-13, 2004-10-15/2004-10-16) transform to ((`[time_field]` ge `2004-10-12` AND `[time_field]` le `2004-10-13`) OR (`[time_field]` ge `2004-10-15` AND `[time_field]` le `2004-10-16`))
As shown in the above examples, all fields and values are written inside back tics (`) - this is the general way of specifying time expressions inside MapServer.
Overall it’s doing a good job and the only thing missing point is that requests like “TIME=2010” and “TIME=2010/2011” should be properly handled.

If you read carefully, MapServer documentation has another slightly different time interpretation for PostGIS layer, to test that I followed up and applied the same requests on a PostGIS layer.

Things I need to configure before I can finally send the same set of WMS time requests to PostGIS layer are listed below:
  • Use “shp2pgsql” command line to convert shape file into pgsql file;
  • Use “psql” command line to load pgsql file into PostGIS database;
  • Add another new column of type “timestamp without time zone”;
  • Copy time value from original column to the new column;
  • Modify map file to use new column of of type “timestamp without time zone” for WMS time;
  • Modify the map file to point to PostGIS table instead of shape file;
All of above are straight forward except that a WMS layer based on PostGIS doesn’t seem to recognize the temporal field if it is of type “character varying”, which force me to add an extra column in type “timestamp without time zone” and copy over those original time values in string. After all of those it worked and below are the results of same WMS TIME requests on shapefile layer:
  • …&TIME=2010 (not recognized, default time value is returned)
  • …&TIME=2010-02 (not recognized, default time value is returned)
  • …&TIME=2010-02-16 (recognized, all points with time stamp within 2010-02-16 returned)
  • …&TIME=2010-02-16T12 (recognized, points between 12:00:00 and 13:00:00 on 2010-02-16 returned, not including points on 13:00:00)
  • …&TIME=2010-02-16T12:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T12:00:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T14:59:59 (recognized, only point of “2010-02-16 14:59:59” returned)
  • …&TIME=2010-02-16T15:00:01 (recognized, only point of “2010-02-16 15:00:01” returned)
  • …&TIME=2010-02-16T15:00:00.000 (recognized, only point of “2010-02-16 15:00:00” returned)
  • …&TIME=2009/2010 (not recognized, default time value is returned)
  • …&TIME=2010/2011 (not recognized, default time value is returned)
  • …&TIME=2010-02/2010-03 (not recognized, default time value is returned)
  • …&TIME=2010-02-16/2010-02-17 (recognized, all points returned)
So it proves what it says in the doc about PostGIS layer:
For PostGIS/PostgreSQL layers, the time expression built uses the date_trunc function available in PostgreSQL. For example, if the user passes a time value of ‘2004-10-12’, the expression set in the filter is date_trunc(‘day’, time_field) = ‘2004-10-12’. The use of the date_trunc function allows requests to use the concept of time resolution. In the example above, for a request of ‘2004-10-12’, MapServer determines that the resolution is “day” by parsing the time string and the result gives all records matching the date 2004-10-12 regardless of the values set for Hours/Minutes/Seconds in the database. For more information on the date_trunc function, please refer to the PostgreSQL documentation.

Thursday, March 4, 2010

Two ambiguities about TIME in OGC WMS specification

OGC WMS specification had included “TIME” dimension since version 1.1.0, in which it guides the WMS implementations on how “TIME” is involved in filtering feature that will finally be visible in either a result map or a query result (aka. GetFeatureInfo result). To me, what it defines about “TIME” dimension in WMS spec seems to be both accurate and ambiguous. It is accurate because it introduces the ISO 8601 standard to cover the exchange of date and time related parameters between a WMS server and a WMS client, which eliminate the risk of misinterpretation where numeric representation of dates and times are interchanged across national boundaries, and to avoid the confusion and other consequential errors or losses (quoted from wiki ISO8601 entry). But it’s also ambiguous in two places (1) where an interpretation is needed for an ISO standard time string; (2) where a temporal attribute has different meanings for different types of features. In this blog let’s skip the accurate part that I am happy with, but discuss the ambiguous part using a few examples. 

The first ambiguity is actually not a problem of WMS spec itself but comes from the nature of the data when a temporal attribute value has different meanings for different types of features. Basically time values can ne divided into two different types: time as a individual time point and time as a time window.  Time as a time point doesn’t have any relation to its history or future, one good example is a rocket feature with a time attribute called “launch” that has a value of “2000-03-04T00:00:00Z”. It means the rocket launched at Mar.4th 2000 at 12 am which has nothing to do with any time before it or after. But time as a time window instead expand backward or forward. One example is a house feature that has time attribute called “built” with a value of “2000”, it actually affects the time after 2000 because the house exists since then. Another example is an animal species with time attribute “extinct” of value “2000”, which actually affects the time before 2000 because the animal species had existed before 2000.  Given the fact that all of those single time values are treated the same in database, it will potentially affect the result of a query on those features.

The second ambiguity in WMS spec, as I mentioned above is the interpretation for a time value. For example, when you send a WMS request like “http://…/…request=GETMAP&…&TIME=2010”, what does it exactly mean? It sounds like a stupid question because everyone knows “2010” meaning year 2010, which is clearly defined in ISO8601 as valid. But how do you interpret this request is ambiguous. Assume you’re a time-enabled WMS server and you have some features with time stamps between 2010 and 2011 (any month, day, hour, minute, or second within 2010), will you include those features in the map response to request “…&TIME=2010”? I bet you answer yes without hesitation because naturally “TIME=2010” means within 2010. But will you give it a second thought if I give a different request “http://…/…request=GETMAP&…&TIME=2010-12-10T12” on the same set of features? Do you still think you should include all the features between 12pm and 1pm on 2010-12-10? I don’t think so but that’s actually the same logic you used when you process request “http://…/…request=GETMAP&…&TIME=2010”.

If you’re implementing a TIME-enabled WMS at server-side you probably already encountered those two problems, and I don’t think the solution is simply throwing the TIME values to database for string comparison even if the database is aware of date/time type. The best way I can think of is always using time window internally (even though sometimes that time window is very small) for both feature temporal attributes and time parameter in the WMS request. To be more specific, when serving out you data if you have a rocket feature launched at “2000-03-04T00:00:00Z” then explicitly treat it as “2000-03-04T00:00:00.000Z/2000-03-04T00:00:00.999Z”; if you have a house built on “2010” then explicitly treat it as “2010/far future”; and finally if you have an animal species extinct in 2000 then explicitly treat it as “long ago/2000”. Now when it comes to time parameter in WMS request, the spec allows syntaxes like “TIME=time1”, “TIME=time_start/time_end”, “TIME=time1,time2” and “TIME=time_start1/time_end1,time_start2/time_end2” but no matter what client sends to you, my suggestion is to always treat each single time value as a time window based on its smallest interval. For example, if “TIME=2000” then treat it as “TIME=2000/2001” (the smallest time interval is year); if “TIME=2000-03-04T00:05Z” treat it as “2000-03-04T00:05Z/2000-03-04T00:06Z” (the smallest time interval is minute) and so on. By using this mechanism every time relate value is a time window, which makes the query more straight forward, just include the feature either in map or query results if the query time window touches, intersects or contains the time window of feature temporal attribute. Although it won’t totally solve the ambiguity above, it helps improve the accuracy.

Monday, February 22, 2010

Setup a http based SVN repository with TortoiseSVN and Apache http server

It is very useful to setup a http based SVN repository on your local machine just for daily check in and out of source code. I am pretty sure there are plenty of materials over the web that explain how to do it for different combinations. Here is the one which I went through and it works pretty good for me.
1. Install and configure TortoiseSVN
2. Install Apache HTTP Server
    • Download Apache HTTP Server from its' website at http://httpd.apache.org/download.cgi#apache22, and the version I’m using is 2.2.14 (either the one with or without ssl); try avoid using 1.x or 3.x which either doesn’t work well or hasn’t been tried by many people. So apache 2.2 is the best pick for now;
    • Install Apache http server by following the wizards;
    • Change the Apache HTTP Server listening port if necessary in conf.
    • After installation, just test it by launching http://localhost:port/ in browser and you should see a “It works!” page; (you can also monitor the apache http service in Apache Service Monitor)
3. Install Subversion
    • Download the latest version of the Subversion Win32 binaries for Apache. Be sure to get the right version to integrate with your version of Apache, otherwise you will get an obscure error message when you try to restart. If you have Apache 2.2.x go to http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100;
    • !!!DON'T!!! use the msi version of subversion (it doesn't work somehow), instead use the zip version; (after unzip the subversion folder to your local disk, you also need to add the bin path to 'path' system environment variable)
    • Using the windows explorer, go to the installation directory of Subversion and find the files /httpd/mod_dav_svn.so and mod_authz_svn.so. Copy these files to the Apache modules directory;
    • Copy the file /bin/libdb*.dll and /bin/intl3_svn.dll from the Subversion installation directory to the Apache bin directory;
4. Configure Subversion and Apache HTTP Server
Edit Apache's configuration file (usually C:\Program Files\Apache Group\Apache2\conf\httpd.conf) with a text editor such as Notepad and make the following changes:
Uncomment (remove the '#' mark) the following lines:
    #LoadModule dav_fs_module modules/mod_dav_fs.so
    #LoadModule dav_module modules/mod_dav.so
Add the following two lines to the end of the LoadModule section.
        LoadModule dav_svn_module modules/mod_dav_svn.so
    LoadModule authz_svn_module modules/mod_authz_svn.so
b. At the end of the config file add the following lines:
    <Location /svn>
         DAV svn
         SVNListParentPath on
         SVNParentPath D:\SVN
         #SVNIndexXSLT "/svnindex.xsl"
         AuthType Basic
         AuthName "Subversion repositories"
         AuthUserFile passwd
         #AuthzSVNAccessFile svnaccessfile
         Require valid-user
     </Location>

c. This configures Apache so that all your Subversion repositories are physically located below D:\SVN. The repositories are served to the outside world from the URL: http://MyServer/svn/ . Access is restricted to known users/passwords listed in the passwd file.

d. To create the passwd file, open the command prompt (DOS-Box) again, change to the apache2 folder (usually c:\program files\apache group\apache2) and create the file by entering

           bin\htpasswd -c passwd <username>

This will create a file with the name passwd which is used for authentication. Additional users can be added with

          bin\htpasswd passwd <username>

e. Restart the Apache service again;

f. Point your browser to http://MyServer/svn/MyNewRepository (where MyNewRepository is the name of the Subversion repository you created before). If all went well you should be prompted for a username and password, then you can see the contents of your repository.
5. Create SVN repository and access it through HTTP
    • Create a folder on your local disk (under the folder where you specify in Apache http server to be the root folder of your SVN);
    • Right click on the folder and use the TortoiseSVN context menu “Create Repository Here”;
    • Access your SVN repository through HTTP as http://server:port/svn/repositoryName/ (you need to type in username and password).

Wednesday, February 3, 2010

Google Latitude finally locates me

I joined Google Latitude more than half year ago but no matter whether I’m at home (Rancho Cucamonga, CA) or at my office (Redlands, CA), it always tells me that I’m in Hague Netherlands, which I assume is what my friends will see too. Recently it finally corrects the location to Rancho Cucamonga or Redlands. Since I am not using Google Latitude on my mobile device, the only way Google Latitude can rely on should be the IP address and associated ISP. Just wonder why there could be such a big mistake.

Friday, January 22, 2010

First experience of armchair mapping using Mapzen and JSOM WMS plug-in

In previous post I shared my first experience of making changes to OSM database with Potlatch and JOSM. As I mentioned there both of them are good tools but neither is perfect so I’m still looking for some alternatives.

Mapzen caught my eye recently probably because it becomes quite a popular buzz word in blogs and news feedings so I decided to give it a try. At first glance, Mapzen looks quite similar to Potlatch (CloudMade who hosts Mapzen is founded by the same person who started OSM):

1. It requires an account (you can register it for free) to work with;

2. A peek at source of Mapzen front page shows it’s based on Flex too;

3. It uses OSM map as base layer in view mode;

4. It uses Yahoo Imagery as based layer in edit mode; (by the way, Yahoo Aerial Imagery is probably the only imagery available for free which has decent globe coverage. I never see other options, do you?)

Mapzen in view mode:

1

Mapzen in edit mode:

2

I like the way it categorize those pre-defined features in Points, Lines and Shapes and each one of the feature types has a meaningful icon associated. Of course it lacks some of the advanced editing functionalities that are available in Potlatch (it seems to have a tool to line up points) and JOSM, but it has more than enough for majority of the users. Another thing I like Mapzen over Potlatch is the look and feel when you draw the geometry on map. With Potlatch I always feel like using a big brush for wall painting, where all I actually need is pencil for sketch. The only weakness though is that I didn’t find a way to upload data as input for my editing, either through an osm file or a gpx track while Potlatch provides that option. Everything must be hand drawn. But anyway you can always combine the power of both.

After editing simply click “Save Map” button at corner (you might want to input some notes for this particular editing). The picture above shows the hand-draw building and foot way of my house using Mapzen, as well as a BBQ island garden “August Town” (named after my boy).

JOSM with its WMS plugin (with the Yahoo Aerial Imagery extension) is another option I tried after Mapzen, which I probably rank the highest among all three of them (Potlatch, Mapzen and JOSM). Like I mentioned before, the lack of a decent base imagery is a huge bottleneck which makes it almost impossible for armchair mapping, but the WMS plug-in with Yahoo Aerial Imagery extension overcomes it like magic. So let’s equip with it first by following the instructions here. A brief list of steps:

Install WMS plugin itself:

With the plugin manager

You can easily install plugins from within JOSM as follows

  1. Start JOSM, open the preferences window (Edit->Preferences or use the toolbar icon) and select the plugins tab.
  2. Click on "Download List" to download the list of available plugins.
  3. Check the plugins you want installed.
  4. Click the accept button. All new plugins should start downloading and installing.
  5. Restart JOSM.
Manually

With older versions (up until 277), you have to install the plugins manually.

  1. Download the plugin file from wherever the plugin is hosted. Look in the plugin page or the 'Plugins' page on the JOSM wiki site for the location of this file.
  2. The file should have an ".jar" extension. If it doesn't, rename the downloaded file so that it ends with ".jar". Internet Explorer, for instance, may rename some files to ".zip".
  3. Move the .jar file to the JOSM preferences directory ("%APPDATA%/JOSM" in windows, "~/.josm/plugins/" in Unix/MacOS.)
  4. Start JOSM, open the preferences window (Edit->Preferences) and select the plugins tab.
  5. Activate the plugin in the plugins tab.
  6. Restart JOSM.

Install Yahoo! Aerial Imagery Downloader

On Windows use the WebKit based downloader called webkit-image as follows

  • Download webkit-image.zip
  • Unzip it.
  • Move the contents so that the DLL files and the EXE file are somewhere "on your system path" (eg. c:\windows ). The best way to achieve this might be to place them alongside josm-latest.jar Keep the 'imageformats' subfolder alongside too (so all the contents of the zip).
  • Restart JOSM
  • Do 'WMS' menu -> YAHOO (Webkit)

You should start to get Yahoo! imagery (may take up to 30 seconds to start showing). If not, it may not be finding the DLL files correctly.

Note: If you don't want to place webkit-image in your system path or the JOSM directory you don't have to. By editing the download program you can specify an absolute or relative path to the webkit-image executable. Examples:

  • webkit/webkit-image {0} - loads webkit-image.exe from the subdirectory webkit relative to the JOSM installation directory
  • D:/webkit/webkit-image {0} - uses webkit-image D:\webkit\webkit-image.exe

After a successful install, restart JOSM and you should a new menu called “WMS” like below:

3

Now download your data from OSM database as usual first, and after select the “WMS” menu and select “Yahoo Sat”. Wait for about 5, 6 seconds you will see a nice aerial imagery base layer underneath your OSM data.

4

Well done! And this is perfect for armchair mapping. Now enjoy your smooth mapping experience with powerful tools in JOSM. I didn’t try out the WMS layer and rectified image yet which can definitely be better base layer options for specific area.

After the editing simply click upload button (right next to download button) and upload the changes. (you may need to set your OSM account username and password)

6

5

So my conclusion is that JOSM with WMS plugin is an obvious winner due to the smooth and rich desktop UI experience if you’re doing armchair mapping at home. But Potlatch and Mapzen have their own advantages of being browser based and easy to use for non-professional users.