-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathosm2geojson.pl
148 lines (116 loc) · 4.31 KB
/
osm2geojson.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/perl
use strict;
use warnings;
use XML::Simple;
use JSON;
use v5.10;
# ---------------------------------------------------------------------------
# Load OSM file
my $Osm_Ref = XMLin(
'data.osm',
ForceArray => 1,
KeyAttr => ['id', 'k'],
ValueAttr => ['ref'],
);
# ---------------------------------------------------------------------------
# Convert all OSM polygons into a hash where the key is the OSM way ID and
# the value is a hash containing the OSM way's name=* tag value (the outline
# group it belongs to) and the way's list of positions.
# Also collect the group colors.
my %Positions_Array_Data;
my %Group_Colors;
foreach my $way_id (keys %{$Osm_Ref->{way}}) {
# Get way Perl data info
my $way_ref = $Osm_Ref->{way}{$way_id};
# Get way's imagery group name and color
my $group_name = $way_ref->{tag}{name }{v} || '';
my $color_code = $way_ref->{tag}{color}{v} || '';
$Group_Colors{$group_name} = $color_code;
# Get array of positions for the way
my @positions_array;
foreach my $node_id (@{$way_ref->{nd}}) {
my $node_ref = $Osm_Ref->{node}{$node_id};
my $lat = $node_ref->{lat};
my $lon = $node_ref->{lon};
push @positions_array, [$lon+0, $lat+0]; # Numerify coordinates
}
# Add to positions array hash
$Positions_Array_Data{$way_id} = {
group => $group_name,
positions_array => \@positions_array,
};
}
# ---------------------------------------------------------------------------
# Convert OSM multipolygons into a list where each item is a hash containing
# the outer way's name=* tag value and an array of each member way's list of
# of positions. This data is obtained and removed from the positions array
# hash earlier.
my @Polygons_Data; # GeoJSON meaning of "Polygon"
foreach my $relation_ref (values %{$Osm_Ref->{relation}}) {
# Skip non-OSM multipolygons
next if !exists $relation_ref->{tag}
|| !exists $relation_ref->{tag}{type}
|| $relation_ref->{tag}{type}{v} ne 'multipolygon';
# Get the positions for the ways of the relation using the positions array
# hash and remove them from that hash. Create an array of position arrays
# for the member ways with the outer way's array as the first item.
# Get the imagery group name too.
my @positions_array_array;
my $group_name;
foreach my $member_ref (@{$relation_ref->{member}}) {
my $member_id = $member_ref->{ref};
my $data_ref = $Positions_Array_Data{$member_id};
my $positions_array_ref = $data_ref->{positions_array};
given ($member_ref->{role}) {
when ('outer') {
unshift @positions_array_array, $positions_array_ref;
$group_name = $data_ref->{group};
}
when ('inner') {
push @positions_array_array, $positions_array_ref;
}
}
delete $Positions_Array_Data{$member_id};
}
# Add to polygons list
push @Polygons_Data, {
group => $group_name,
positions_array_array => \@positions_array_array,
};
}
# ------------------------------------------------------------------------
# Initialize hash of GeoJSON features, 1 item for each of the known groups
my %Features;
foreach my $group_name (keys %Group_Colors) {
# Skip empty group names (created from OSM inner polygons)
next if $group_name eq '';
$Features{$group_name} = {
type => 'Feature',
id => $group_name,
geometry => {
type => 'MultiPolygon',
coordinates => [],
},
properties => {
color => $Group_Colors{$group_name},
}
};
}
# Process GeoJSON simple Polygon objects
foreach my $data_ref (values %Positions_Array_Data) {
my $group_name = $data_ref->{group};
push @{$Features{$group_name}{geometry}{coordinates}}, [$data_ref->{positions_array}];
}
# Process GeoJSON multiple-ring Polygon objects
foreach my $data_ref (@Polygons_Data) {
my $group_name = $data_ref->{group};
push @{$Features{$group_name}{geometry}{coordinates}}, $data_ref->{positions_array_array};
}
# Construct final GeoJSON structure
my %Json = (
type => 'FeatureCollection',
features => [@Features{sort keys %Features}],
);
# Output GeoJSON
open my $fh, '>', 'data.geojson' or die ($!);
print {$fh} JSON->new->pretty->encode(\%Json);