WikiProject Falkland Islands/Converting Government-supplied data

From OpenStreetMap Wiki
Jump to navigation Jump to search

The Falkland Islands Government supplied data to OpenStreetMap in .dxf format, exported from AutoCAD. This page explains how this data was converted into .osm files for upload to OpenStreetMap.

First, the files were converted into shapefiles using the instructions at Convert dxf File to Shapefile Using Grass

The Shapefiles were then converted to .osm format using the following Python script, adapted from the one at http://boston.freemap.in/osm/files/mgis_to_osm.py

    #!/usr/bin/python
    # Hacked together from script at http://boston.freemap.in/osm/files/mgis_to_osm.py
    # On Fedora 7, needs gdal-python package & create symlink from /usr/lib/libproj.so.0.5.2 to /usr/lib/libproj.so

   VERSION="0.1"

   import ogr

   def parse_shp_for_massgis( filename ):
        #ogr.RegisterAll()

       dr = ogr.GetDriverByName("ESRI Shapefile")
        poDS = dr.Open( filename )

       if poDS == None:
            raise "Open failed."

       poLayer = poDS.GetLayer( 0 )

       poLayer.ResetReading()

       ret = []

       poFeature = poLayer.GetNextFeature()
        while poFeature:
            tags = {}

           # SEGMENT ID
            tags["massgis:seg_id"] = 2
            # STREET ID
            tags["massgis:way_id"] = 1

           # COPY DOWN THE GEOMETRY
            geom = []
                
            rawgeom = poFeature.GetGeometryRef()
            #print dir( rawgeom )
            for i in range( rawgeom.GetPointCount() ):
                geom.append( (rawgeom.GetX(i), rawgeom.GetY(i)) )
        
            ret.append( (geom, tags) )
            poFeature = poLayer.GetNextFeature()
            
        return ret

   import osr

   fk_wkt = \
    """PROJCS["UTM Zone 21, Southern Hemisphere",
        GEOGCS["International 1909 (Hayford)",
            DATUM["D_unknown",
                SPHEROID["intl",6378388,297]],
            PRIMEM["Greenwich",0],
            UNIT["Degree",0.017453292519943295]],
        PROJECTION["Transverse_Mercator"],
        PARAMETER["latitude_of_origin",0],
        PARAMETER["central_meridian",-57],
        PARAMETER["scale_factor",0.9996],
        PARAMETER["false_easting",500000],
        PARAMETER["false_northing",10000000],
        UNIT["Meter",1]]"""

   from_proj = osr.SpatialReference()
    from_proj.ImportFromWkt( fk_wkt )

   to_proj = osr.SpatialReference()
    to_proj.SetWellKnownGeogCS( "EPSG:4326" )

   tr = osr.CoordinateTransformation( from_proj, to_proj )

   def unproject( point ):
        pt = tr.TransformPoint( point[0], point[1] )
        return (pt[1], pt[0])

   def round_point( point, accuracy=2 ):
        return tuple( round(x,accuracy) for x in point )

   def compile_nodelist( parsed_massgis, first_id=1 ):
        nodelist = {}
        
        i = first_id
        for geom, tags in parsed_massgis:
            if len( geom )==0:
                continue
            
            for point in geom:
                r_point = round_point( point )
                if r_point not in nodelist:
                    nodelist[ r_point ] = (i, unproject( point ))
                    i += 1
                
        return (i, nodelist)

   def adjacent( left, right ):
        left_left = round_point(left[0])
        left_right = round_point(left[-1])
        right_left = round_point(right[0])
        right_right = round_point(right[-1])
        
        return ( left_left == right_left or
                 left_left == right_right or
                 left_right == right_left or
                 left_right == right_right )
                 
    def glom( left, right ):
        
        left = list( left )
        right = list( right )
        
        left_left = round_point(left[0])
        left_right = round_point(left[-1])
        right_left = round_point(right[0])
        right_right = round_point(right[-1])
        
        if left_left == right_left:
            left.reverse()
            return left[:-1] + right
            
        if left_left == right_right:
            return right[:-1] + left
            
        if left_right == right_left:
            return left[:-1] + right
            
        if left_right == right_right:
            right.reverse()
            return left[:-1] + right
            
        raise 'segments are not adjacent'

   def glom_once( segments ):
        if len(segments)==0:
            return segments
        
        unsorted = list( segments )
        x = unsorted.pop(0)
        
        while unsorted:
            for i in range( len( unsorted ) ):
                y = unsorted[i]
                if adjacent( x, y ):
                    y = unsorted.pop(i)
                    x = glom( x, y )
                    break
            # Sorted and unsorted lists have no adjacent segments
            else:
                break
                
        return x, unsorted
        
    def glom_all( segments ):
        unsorted = segments
        chunks = []
        
        while unsorted:
            chunk, unsorted = glom_once( unsorted )
            chunks.append( chunk )
            
        return chunks
            
                    

   def compile_waylist( parsed_massgis, blank_way_id ):
        waylist = {}
        
        #Group by massgis:way_id
        for geom, tags in parsed_massgis:
            way_key = tags.copy()
            del way_key['massgis:seg_id']
            way_key = ( way_key['massgis:way_id'], tuple( way_key.iteritems() ) )
            
            if way_key not in waylist:
                waylist[way_key] = []
                
            waylist[way_key].append( geom )
        
        ret = {}
        for (way_id, way_key), segments in waylist.iteritems():
            
            if way_id != blank_way_id:
                ret[way_key] = glom_all( segments )
            else:
                ret[way_key] = segments
            
        return ret
                

   import time
    from xml.sax.saxutils import escape
    def massgis_to_osm( massgis_filename, osm_filename, blank_way_id ):
        
        import_guid = time.strftime( '%Y%m%d%H%M%S' )

       print "parsing shpfile"
        parsed_features = parse_shp_for_massgis( massgis_filename )
        
        print "compiling nodelist"
        i, nodelist = compile_nodelist( parsed_features )
        
        print "compiling waylist"
        waylist = compile_waylist( parsed_features, blank_way_id )
        
        print "constructing osm xml file"
        ret = []
        ret.append( "<?xml version='1.0' encoding='UTF-8'?>" )
        ret.append( "<osm version='0.5' generator='JOSM'>" )
        
        for id, (lat, lon) in nodelist.values():
            ret.append( "  <node id='-%d' action='create' visible='true' lat='%f' lon='%f' >" % (id, lat, lon) )
            #ret.append( "    <tag k=\"source\" v=\"massgis_import_v%s_%s\" />" % (VERSION, import_guid) )
            #ret.append( "    <tag k=\"attribution\" v=\"Office of Geographic and Environmental Information (MassGIS)\" />" )
            ret.append( "  </node>" )
            
        for waykey, segments in waylist.iteritems():
            for segment in segments:
                ret.append( "  <way id='-%d' action='modify' visible='true'>" % i )
                
                ids = [ nodelist[ round_point( point ) ][0] for point in segment ]
                for id in ids:
                    ret.append( "    <nd ref='-%d' />" % id )
                    
                # Following two lines add tags to the ways - comment them out if not required, or edit to suit
                ret.append( "    <tag k='highway' v='unsurfaced' />" )
                ret.append( "    <tag k='source' v='Falkland Islands Government (Public Works Department)' />" )
                
                ret.append( "  </way>" )
                
                i += 1
            
        ret.append( "</osm>" )
        
        print "writing to disk"
        fp = open( osm_filename, "w" )
        fp.write( "\n".join( ret ) )
        fp.close()
        
    if __name__ == '__main__':
        import sys, os.path
        if len(sys.argv) < 2:
            print "%s filename.shp [filename.osm]" % sys.argv[0]
            sys.exit()
        shape = sys.argv[1]
        if len(sys.argv) > 2:
            osm = sys.argv[2]
        else:
            osm = shape[:-4] + ".osm" 
        id = os.path.basename(shape).split("_")[-1]    
        massgis_to_osm( shape, osm, id )