Jun 23 2008

How to automatically version a Flex application

Tag: code, pythondenis @ 4:11 pm

I recently added automatic versioning to my Flex projects. This is quite useful when you want to know exactly which version of your application is currently running when it’s being debugged, deployed, tested, etc. This basic example will generate a version string containing a major version number, minor version number, svn revision number and a build number. The build number is incremented every time your application is built. It assumes you have python installed and use SVN as your version control system.

Storing your version information

We will use a simple text file to do that and we’ll call it .version. Its format will be:
major_no
minor_no
build_no
revision_no

Updating the version data

A small python script is used to do that. A build number is incremented each time and the revision number is retrieved from SVN. Here’s the versioner.py script (make sure it’s executable):

#!/usr/bin/python
v = open('.version')
major = int(v.readline().strip() or '2') # can change 2 for any major version number you'd like
minor = int(v.readline().strip() or '5') # idem
build = int(v.readline().strip() or '0') # idem
build += 1
import os, re
ri, ro = os.popen2("svn info", 'r')
rev = ro.read()
ri.close()
ro.close()
revre = re.compile('Revision: ([\d]+)')
revision = revre.search(rev).group(1)
o = open('.version', 'w')
o.write("%d\n%d\n%d\n%s\n" % (major, minor, build, revision))
o.close()

Running the version script

If you’re using Flex Builder, this is easily done:
Go to project properties.
Under the Builders tab, click New
Name it anything you like
Select the versioner.py script in location
Select the folder containing your .version file as the working directory
Check “During manual builds” and “During auto builds” in the Build Options
Once your builder is created, make sure it’s at the top.

Using the version information

You can embed it in your Flex application using the following code:

[Embed(source="../.version",mimeType="application/octet-stream")][Bindable]
 
public var version:Class;

and then you can access it at runtime using a function like this:

public function getVersion():String {var v:Array = String(new version).split('\n');
 
return v[0] + '.' + v[1] + '.' + v[3] + ' build ' + v[2];;
 
}

which will return something like ‘major.minor.revision build’.


Apr 02 2008

downloading google maps tiles using modestmaps

Tag: codedenis @ 1:14 pm

ModestMaps is one the nicest mapping API out there: it just feels right. I especially love the python version, which I used to download some Google Maps tiles (anyone remember gMerge?). The following script gets a Google Maps permalink and downloads the tiles all the way down to the maximum zoom level. You may need to alter the file retrieval and naming logic. The beauty here lies in the fact that ModestMaps allows you to download any layer from many APIs (Google, Virtual Earth, Yahoo Maps and OpenStreetMaps) by changing the Google.AerialProvider() call.

import sys, urllib2, re,os, Image, httplib
 
def parselink(link):
 qs = link[link.index('?')+1:]
 vals = [v.split('=') for v in qs.split('&')]
 params = {}
 for v in vals:
  params[v[0]] = v[1]
 zoom = int(params['z'] or '0')
 center = map(float, params['ll'].split(','))
 span = map(float, params['spn'].split(','))
 ne, sw = list(center), list(center)
 ne[0] += span[0]
 ne[1] += span[1]
 sw[0] -= span[0]
 sw[1] -= span[1]
 return ne, sw, zoom
 
def fetch(pt):
 if pt.zoom > Core.Coordinate.MAX_ZOOM:
  return
 
 if download(pt):
  z = pt.zoomBy(1)
  fetch(z)
  fetch(z.right())
  fetch(z.down())
  fetch(z.down().right())
 
def download(pt):
  url = layer.getTileUrls(pt)[0]
  try:
   os.makedirs("tiles/%d/%d" % (pt.zoom, pt.row))
  except: pass
  fn = "tiles/%d/%d/%d_%d.jpg" % (pt.zoom, pt.row, pt.row, pt.column)
  if os.path.exists(fn):
   try:
    m = Image.open(fn)
    a,b,c,d = m.getbbox()
    return d > 1
   except:
    return False
  print url
  try:
   # should use urllib2 to fake user agent here
   data = urllib.urlopen(url).read()
   out = open(fn, 'w')
   out.write(data)
   out.close()
   return True
  except urllib2.HTTPError, e:
   if e.code == 404:
    #empty tile
    out = Image.new('RGB', (1,1))
    out.save(fn)
    return False
 
  #should not reach this point. add better error handling
  sys.exit(-1)
 
if __name__ == "__main__":
 sys.path.append('trunk/py/')
 from ModestMaps import *
 
 layer = Google.AerialProvider()
 bbox = parselink('http://maps.google.com/maps?f=q&hl=en&geocode=&q=new++york,+ny&ie=UTF8&ll=40.75506,-73.969917&spn=0.100907,0.197926&t=h&z=13&iwloc=addr')
 ne = layer.locationCoordinate(Geo.Location(bbox[0][0], bbox[0][1])).zoomTo(bbox[-1]).container()
 sw = layer.locationCoordinate(Geo.Location(bbox[1][0], bbox[1][1])).zoomTo(bbox[-1]).container()
 cur = ne.copy()
 while cur.row <= sw.row:
   while cur.column >= sw.column:
    fetch(cur.copy())
    cur = cur.left()
   cur.column = ne.column
   cur = cur.down()

Or get the file here


Nov 22 2007

wwjava in fullscreen

Tag: codedenis @ 1:28 pm

to have wwjava run in fullscreen, try the following:

  • - instantiate your worlwind JFrame (WWFrame in supplied demos)
  • - make it non-resizable (wwFrame.setResizable(false);)
  • - finally type the following incantation:
GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
graphicsDevice.setFullScreenWindow(wwFrame);
wwFrame.setVisible(true);

where wwFrame is your worldwind frame.


Nov 22 2007

displaying shapefiles in wwjava

Tag: codedenis @ 3:48 am

here is a quick hack to add esri shapefiles support in worlwind java:

1- get gistoolkit
2- strip de bones (keep only gistoolkit.features.*, gistoolkit.common and gistoolkit.datasources.shapefile)

3 - Link to your project.

4- then you can add a shapefile to a layer by doing something like this, if you are really lazy:

ShapeFile shp = new ShapeFile(fn);
if (shp == null)
return;
try {
shp.readRecords();
} catch(Exception e) {
err("Error while reading shape.");
return;
}
ShapeFileRecord[] r = shp.getRecords();
int i, j;
fireLayer.clearList();
gistoolkit.features.Point lastCenter = null;
for(i=0; i&lt;r.length; i++)
{
gistoolkit.features.Shape s = r[i].getShape();
if (s.getShapeType() == "MULTIPOLYGON") {
gistoolkit.features.MultiPolygon m = (gistoolkit.features.MultiPolygon) s;
gistoolkit.features.Polygon[] polys = m.getPolygons();
for(j=0; j&lt;polys.length; j++) {
addPolygon(polys[j]);
lastCenter = polys[j].getCentroid();
}
}
}
 
private void addPolygon(gistoolkit.features.Polygon p)
{
int i;
ArrayList&lt;LatLon&gt; positions = new ArrayList&lt;LatLon&gt;();
gistoolkit.features.Point[] pts = p.getPoints();
for(i=0; i&lt;pts.length; i++)
{
positions.add(LatLon.fromDegrees(pts[i].y, pts[i].x));
}
Dimension texSize = new Dimension();
texSize.setSize(1024, 1024);
SurfacePolygon fire = new SurfacePolygon(positions, new Color(1f, 0f, 0f), new Color(1f, 0f, 0f), texSize);
fire.setDrawInterior(true);
fireLayer.addRenderable(fire);
}

fireLayer is a RenderableLayer that was initialized previously.

texSize is needed so that the shape doesn’t look too blurry.