OSM ocaml parser

From OpenStreetMap

Jump to: navigation, search
Image:No05.png Software described on this page or in this section is unlikely to be compatible with API version 0.5 deployed on 8 October, 2007. If you have fixed the software, or concluded that this notice does not apply, remove it.

For handling OSM data in ocaml (good starting point to write converters and/or renderers). The code in this page is licensed under the WTFPL license.


Osm.ml is simple internal form (supports only basic stuff: time-stamps, user and visible attributes are ignored):

type id = string

module SimpleString =  struct 
  type t = string 
  let compare = Pervasives.compare 
end

module StringMap = Map.Make(SimpleString)

type tags = string StringMap.t

type node = {n_id:id;n_tags: tags; n_lat_long:float*float}

type segment = {s_id:id; s_tags: tags; s_from_to: id*id}

type way = {w_id: id; w_tags: tags; w_segs: id list; }


type nodes = node StringMap.t
type segments = segment StringMap.t
type ways = way StringMap.t

type osm = {o_nodes:nodes;o_segments:segments;o_ways:ways} 
let empty_world = {o_nodes=StringMap.empty; o_segments=StringMap.empty; o_ways=StringMap.empty}

let add_tag k v tag_map = StringMap.add k v tag_map
let no_tag = StringMap.empty

let new_node id tags ll =  {n_id=id;n_tags=tags;n_lat_long=ll}
let register_node n osm = {osm with o_nodes= StringMap.add n.n_id n osm.o_nodes}
  
let new_segment id tags from_to =  {s_id=id;s_tags=tags;s_from_to=from_to}
let register_segment s osm = {osm with o_segments= StringMap.add s.s_id s osm.o_segments}

let new_way id tags segs =  {w_id=id;w_tags=tags;w_segs=segs}
let register_way w osm = {osm with o_ways= StringMap.add w.w_id w osm.o_ways}

(* access *)

let get_node osm id = StringMap.find id osm.o_nodes
let get_segment osm id = StringMap.find id osm.o_segments
let get_way osm id = StringMap.find id osm.o_ways

let fold_ways func osm acc =
  StringMap.fold func osm.o_ways acc

let get_tag tags k = StringMap.find k tags
let has_tag tags k = StringMap.mem k tags
let way_tag w k = get_tag w.w_tags k
let way_has_tag w k = has_tag w.w_tags k
let way_has_tag_in w k_list = 
  List.fold_left (fun has k ->
    has || way_has_tag w k) false k_list

let nodes_of_segment s osm = (get_node osm (fst s.s_from_to),get_node osm (snd s.s_from_to))
let coords_of_segment s osm = 
  let _from,_to = nodes_of_segment s osm in
    _from.n_lat_long,_to.n_lat_long

let segments_of_way w osm = List.fold_left (fun acc s_id -> get_segment osm s_id::acc) [] w.w_segs


(* geometric helpers *)
let continuous_runs w osm = 
  try (
    let segs =  segments_of_way w osm in
    let first_point,_ = coords_of_segment (List.hd segs) osm in
    let runs,last_point = List.fold_left (fun acc s ->
      let runs ,previous_end = acc in
	  begin
	    let _from,_to = coords_of_segment s osm in
	      if _from = previous_end then
		(match runs with
		  |curent::old-> (_to::curent)::old
		  |[]-> [[_from;_to]]
		),_to
	      else ([_from;_to]::runs),previous_end
	  end
    ) ([],first_point) segs
  in
      runs
  ) with |_ -> []




let is_area way osm = 
  try (
    let segs =  segments_of_way way osm in
    let first_point,_ = coords_of_segment (List.hd segs) osm in
    let is_continuous,last_point = List.fold_left (fun acc s ->
      let is_c,previous_end = acc in
	if not is_c then acc else
	  begin
	    let _from,_to = coords_of_segment s osm in
	      if _from = previous_end then
		true, _to
	      else false,previous_end
	  end
    ) (true,first_point) segs
  in
    is_continuous && (last_point = first_point)
  ) with |_ -> false

(* layer management helpers *)

type 'a layers = ('a list )StringMap.t
let empty_layers = StringMap.empty


let add_elem_to_layer se layer_id layers =
  let new_layer = try (
    se::(StringMap.find layer_id layers)
  ) with | Not_found -> [se]
  in
    StringMap.add layer_id new_layer layers
let map_layers f layers = 
  let res = ref [] in  
  StringMap.iter (fun lid elems -> res:= f lid elems:: !res) layers;
    List.rev !res    

and an xml "parser", depending on xml-light:

open Xml
open Osm

let parse_tag tag_map = function
  | Element("tag",["k",key;"v",value],[]) 
  | Element("tag",["v",value;"k",key],[]) -> add_tag key value tag_map
  | _ -> tag_map

let parse_tags xml= 
    List.fold_left parse_tag no_tag xml

let parse_node atts children =
  let r_id = ref "" in
  let r_lat = ref 0. in
  let r_long = ref 0. in
  List.iter (function
    |("id",id) -> r_id := id
    |("lat",lat) -> r_lat := float_of_string lat
    |("lon",long) -> r_long := float_of_string long
    |_ -> ()
  ) atts;
    new_node !r_id (parse_tags children) ( !r_lat , !r_long)


let parse_segment atts children =
  let r_id = ref "" in
  let r_from = ref "" in
  let r_to = ref "" in
  List.iter (function
    |("id",id) -> r_id := id
    |("from",from) -> r_from := from
    |("to",x_to) -> r_to := x_to
    |_ -> ()
  ) atts;
    new_segment !r_id (parse_tags children) (!r_from,!r_to)


let parse_way atts children =
  let r_id = ref "" in
  List.iter (function
    |("id",id) -> r_id := id
    |_ -> ()
  ) atts;
    let r_segs = ref [] in
      List.iter (function
	|Element("seg",["id",id],[]) -> r_segs := id:: !r_segs
	|Element("seg",_,_) -> failwith "bad tag format"
	|_ -> ()
      ) children;
      new_way !r_id (parse_tags children) !r_segs


let parse_osm world= function
  |Element("node",atts, children) -> 
     let n = parse_node atts children in
       register_node n world
  |Element("segment",atts,children) -> 
     let s = parse_segment atts children in
       register_segment s world
  |Element("way",atts, children) ->
     let w = parse_way atts children in
       register_way w world
  |_ -> failwith "unknown tag"
let parse fname=
  match parse_file fname with
    |Element("osm",_,children) -> List.fold_left parse_osm empty_world children
    | _ -> failwith "bad osm format"
Personal tools
recent changes