2
2
3
3
import de .komoot .photon .PhotonDoc ;
4
4
import de .komoot .photon .nominatim .model .*;
5
- import org .apache .commons .dbcp2 .BasicDataSource ;
6
5
import org .locationtech .jts .geom .Geometry ;
7
6
import org .slf4j .Logger ;
8
- import org .springframework .jdbc .core .JdbcTemplate ;
9
- import org .springframework .jdbc .core .RowCallbackHandler ;
10
- import org .springframework .jdbc .core .RowMapper ;
11
7
12
- import java .sql .ResultSet ;
13
- import java .sql .SQLException ;
14
8
import java .sql .Types ;
15
- import java .util .*;
9
+ import java .util .List ;
10
+ import java .util .Map ;
16
11
17
12
/**
18
13
* Importer for data from a Nominatim database.
19
14
*/
20
15
public class NominatimImporter extends NominatimConnector {
21
16
private static final Logger LOGGER = org .slf4j .LoggerFactory .getLogger (NominatimImporter .class );
22
17
23
- // One-item cache for address lookup. Speeds up rank 30 processing.
24
- private long parentPlaceId = -1 ;
25
- private List <AddressRow > parentTerms = null ;
26
-
27
18
public NominatimImporter (String host , int port , String database , String username , String password ) {
28
19
this (host , port , database , username , password , new PostgisDataAdapter ());
29
20
}
@@ -33,52 +24,6 @@ public NominatimImporter(String host, int port, String database, String username
33
24
}
34
25
35
26
36
- List <AddressRow > getAddresses (PhotonDoc doc ) {
37
- RowMapper <AddressRow > rowMapper = (rs , rowNum ) -> new AddressRow (
38
- dbutils .getMap (rs , "name" ),
39
- rs .getString ("class" ),
40
- rs .getString ("type" ),
41
- rs .getInt ("rank_address" )
42
- );
43
-
44
- AddressType atype = doc .getAddressType ();
45
-
46
- if (atype == null || atype == AddressType .COUNTRY ) {
47
- return Collections .emptyList ();
48
- }
49
-
50
- List <AddressRow > terms = null ;
51
-
52
- if (atype == AddressType .HOUSE ) {
53
- long placeId = doc .getParentPlaceId ();
54
- if (placeId != parentPlaceId ) {
55
- parentTerms = template .query (SELECT_COLS_ADDRESS
56
- + " FROM placex p, place_addressline pa"
57
- + " WHERE p.place_id = pa.address_place_id and pa.place_id = ?"
58
- + " and pa.cached_rank_address > 4 and pa.address_place_id != ? and pa.isaddress"
59
- + " ORDER BY rank_address desc, fromarea desc, distance asc, rank_search desc" ,
60
- rowMapper , placeId , placeId );
61
-
62
- // need to add the term for the parent place ID itself
63
- parentTerms .addAll (0 , template .query (SELECT_COLS_ADDRESS + " FROM placex p WHERE p.place_id = ?" ,
64
- rowMapper , placeId ));
65
- parentPlaceId = placeId ;
66
- }
67
- terms = parentTerms ;
68
-
69
- } else {
70
- long placeId = doc .getPlaceId ();
71
- terms = template .query (SELECT_COLS_ADDRESS
72
- + " FROM placex p, place_addressline pa"
73
- + " WHERE p.place_id = pa.address_place_id and pa.place_id = ?"
74
- + " and pa.cached_rank_address > 4 and pa.address_place_id != ? and pa.isaddress"
75
- + " ORDER BY rank_address desc, fromarea desc, distance asc, rank_search desc" ,
76
- rowMapper , placeId , placeId );
77
- }
78
-
79
- return terms ;
80
- }
81
-
82
27
/**
83
28
* Parse every relevant row in placex and location_osmline
84
29
* for the given country. Also imports place from county-less places.
@@ -105,21 +50,74 @@ public void readCountry(String countryCode, ImportThread importThread) {
105
50
sqlArgTypes = new int []{Types .VARCHAR };
106
51
}
107
52
53
+ NominatimAddressCache addressCache = new NominatimAddressCache ();
54
+ addressCache .loadCountryAddresses (template , dbutils , countryCode );
55
+
108
56
final PlaceRowMapper placeRowMapper = new PlaceRowMapper (dbutils );
109
- template .query (SELECT_COLS_PLACEX + " FROM placex " +
110
- " WHERE linked_place_id IS NULL AND centroid IS NOT NULL AND " + countrySQL +
111
- " ORDER BY geometry_sector, parent_place_id" ,
57
+ // First read ranks below 30, independent places
58
+ template .query (
59
+ "SELECT place_id, osm_type, osm_id, class, type, name, postcode," +
60
+ " address, extratags, ST_Envelope(geometry) AS bbox, parent_place_id," +
61
+ " linked_place_id, rank_address, rank_search, importance, country_code, centroid," +
62
+ dbutils .jsonArrayFromSelect (
63
+ "address_place_id" ,
64
+ "FROM place_addressline pa " +
65
+ " WHERE pa.place_id = p.place_id AND isaddress" +
66
+ " ORDER BY cached_rank_address DESC" ) + " as addresslines" +
67
+ " FROM placex p" +
68
+ " WHERE linked_place_id IS NULL AND centroid IS NOT NULL AND " + countrySQL +
69
+ " AND rank_search < 30" +
70
+ " ORDER BY geometry_sector, parent_place_id" ,
112
71
sqlArgs , sqlArgTypes , rs -> {
113
72
final PhotonDoc doc = placeRowMapper .mapRow (rs , 0 );
73
+ final Map <String , String > address = dbutils .getMap (rs , "address" );
74
+
114
75
assert (doc != null );
115
76
116
- final Map <String , String > address = dbutils .getMap (rs , "address" );
77
+ final var addressPlaces = addressCache .getAddressList (rs .getString ("addresslines" ));
78
+ completePlace (doc , addressPlaces );
79
+ doc .address (address ); // take precedence over computed address
80
+ doc .setCountry (cnames );
81
+
82
+ var result = NominatimResult .fromAddress (doc , address );
117
83
84
+ if (result .isUsefulForIndex ()) {
85
+ importThread .addDocument (result );
86
+ }
87
+ });
88
+
89
+ // Next get all POIs/housenumbers.
90
+ template .query (
91
+ "SELECT p.place_id, p.osm_type, p.osm_id, p.class, p.type, p.name, p.postcode," +
92
+ " p.address, p.extratags, ST_Envelope(p.geometry) AS bbox, p.parent_place_id," +
93
+ " p.linked_place_id, p.rank_address, p.rank_search, p.importance, p.country_code, p.centroid," +
94
+ " parent.class as parent_class, parent.type as parent_type," +
95
+ " parent.rank_address as parent_rank_address, parent.name as parent_name, " +
96
+ dbutils .jsonArrayFromSelect (
97
+ "address_place_id" ,
98
+ "FROM place_addressline pa " +
99
+ " WHERE pa.place_id IN (p.place_id, coalesce(p.parent_place_id, p.place_id)) AND isaddress" +
100
+ " ORDER BY cached_rank_address DESC, pa.place_id = p.place_id DESC" ) + " as addresslines" +
101
+ " FROM placex p LEFT JOIN placex parent ON p.parent_place_id = parent.place_id" +
102
+ " WHERE p.linked_place_id IS NULL AND p.centroid IS NOT NULL AND p." + countrySQL +
103
+ " AND p.rank_search = 30 " +
104
+ " ORDER BY p.geometry_sector" ,
105
+ sqlArgs , sqlArgTypes , rs -> {
106
+ final PhotonDoc doc = placeRowMapper .mapRow (rs , 0 );
107
+ final Map <String , String > address = dbutils .getMap (rs , "address" );
118
108
119
- completePlace (doc );
120
- // Add address last, so it takes precedence.
121
- doc .address (address );
109
+ assert (doc != null );
122
110
111
+ final var addressPlaces = addressCache .getAddressList (rs .getString ("addresslines" ));
112
+ if (rs .getString ("parent_class" ) != null ) {
113
+ addressPlaces .add (0 , new AddressRow (
114
+ dbutils .getMap (rs , "parent_name" ),
115
+ rs .getString ("parent_class" ),
116
+ rs .getString ("parent_type" ),
117
+ rs .getInt ("parent_rank_address" )));
118
+ }
119
+ completePlace (doc , addressPlaces );
120
+ doc .address (address ); // take precedence over computed address
123
121
doc .setCountry (cnames );
124
122
125
123
var result = NominatimResult .fromAddress (doc , address );
@@ -130,32 +128,50 @@ public void readCountry(String countryCode, ImportThread importThread) {
130
128
});
131
129
132
130
final OsmlineRowMapper osmlineRowMapper = new OsmlineRowMapper ();
133
- template .query ((hasNewStyleInterpolation ? SELECT_OSMLINE_NEW_STYLE : SELECT_OSMLINE_OLD_STYLE ) +
134
- " FROM location_property_osmline" +
135
- " WHERE startnumber is not null AND " + countrySQL +
136
- " ORDER BY geometry_sector, parent_place_id" ,
131
+ template .query (
132
+ "SELECT p.place_id, p.osm_id, p.parent_place_id, p.startnumber, p.endnumber, p.postcode, p.country_code, p.linegeo," +
133
+ (hasNewStyleInterpolation ? " p.step," : " p.interpolationtype," ) +
134
+ " parent.class as parent_class, parent.type as parent_type," +
135
+ " parent.rank_address as parent_rank_address, parent.name as parent_name, " +
136
+ dbutils .jsonArrayFromSelect (
137
+ "address_place_id" ,
138
+ "FROM place_addressline pa " +
139
+ " WHERE pa.place_id IN (p.place_id, coalesce(p.parent_place_id, p.place_id)) AND isaddress" +
140
+ " ORDER BY cached_rank_address DESC, pa.place_id = p.place_id DESC" ) + " as addresslines" +
141
+ " FROM location_property_osmline p LEFT JOIN placex parent ON p.parent_place_id = parent.place_id" +
142
+ " WHERE startnumber is not null AND p." + countrySQL +
143
+ " ORDER BY p.geometry_sector, p.parent_place_id" ,
137
144
sqlArgs , sqlArgTypes , rs -> {
138
- final PhotonDoc doc = osmlineRowMapper .mapRow (rs , 0 );
139
-
140
- completePlace (doc );
141
- doc .setCountry (cnames );
142
-
143
- final Geometry geometry = dbutils .extractGeometry (rs , "linegeo" );
144
- final NominatimResult docs ;
145
- if (hasNewStyleInterpolation ) {
146
- docs = NominatimResult .fromInterpolation (
147
- doc , rs .getLong ("startnumber" ), rs .getLong ("endnumber" ),
148
- rs .getLong ("step" ), geometry );
149
- } else {
150
- docs = NominatimResult .fromInterpolation (
151
- doc , rs .getLong ("startnumber" ), rs .getLong ("endnumber" ),
152
- rs .getString ("interpolationtype" ), geometry );
153
- }
145
+ final PhotonDoc doc = osmlineRowMapper .mapRow (rs , 0 );
146
+
147
+ final var addressPlaces = addressCache .getAddressList (rs .getString ("addresslines" ));
148
+ if (rs .getString ("parent_class" ) != null ) {
149
+ addressPlaces .add (0 , new AddressRow (
150
+ dbutils .getMap (rs , "parent_name" ),
151
+ rs .getString ("parent_class" ),
152
+ rs .getString ("parent_type" ),
153
+ rs .getInt ("parent_rank_address" )));
154
+ }
155
+ completePlace (doc , addressPlaces );
154
156
155
- if (docs .isUsefulForIndex ()) {
156
- importThread .addDocument (docs );
157
- }
158
- });
157
+ doc .setCountry (cnames );
158
+
159
+ final Geometry geometry = dbutils .extractGeometry (rs , "linegeo" );
160
+ final NominatimResult docs ;
161
+ if (hasNewStyleInterpolation ) {
162
+ docs = NominatimResult .fromInterpolation (
163
+ doc , rs .getLong ("startnumber" ), rs .getLong ("endnumber" ),
164
+ rs .getLong ("step" ), geometry );
165
+ } else {
166
+ docs = NominatimResult .fromInterpolation (
167
+ doc , rs .getLong ("startnumber" ), rs .getLong ("endnumber" ),
168
+ rs .getString ("interpolationtype" ), geometry );
169
+ }
170
+
171
+ if (docs .isUsefulForIndex ()) {
172
+ importThread .addDocument (docs );
173
+ }
174
+ });
159
175
160
176
}
161
177
@@ -164,8 +180,7 @@ public void readCountry(String countryCode, ImportThread importThread) {
164
180
*
165
181
* @param doc
166
182
*/
167
- private void completePlace (PhotonDoc doc ) {
168
- final List <AddressRow > addresses = getAddresses (doc );
183
+ private void completePlace (PhotonDoc doc , List <AddressRow > addresses ) {
169
184
final AddressType doctype = doc .getAddressType ();
170
185
for (AddressRow address : addresses ) {
171
186
AddressType atype = address .getAddressType ();
0 commit comments