Skip to content

Commit

Permalink
Change z_order of link roads and add tests
Browse files Browse the repository at this point in the history
Mosat maps prefer placing link roads below non-link roads. This allows
them to do so without complex SQL or multiple layer solutions which
break layer tag usage.

z_order per layer has also been increased from 10 to 100 to allow
for this more fine-grained ordering.

Other types of roads (tracks, paths, etc) have been added. This
does not impact those not using z_order for layers with these, but
does allow their use.
  • Loading branch information
pnorman committed Jul 5, 2015
1 parent b2bf4fc commit a2f5b94
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ tests/test-output-multi-polygon
tests/test-output-multi-poly-trivial
tests/test-output-pgsql
tests/test-output-pgsql-tablespace
tests/test-output-pgsql-z_order
tests/test-expire-tiles
tests/*.log
tests/*.trs
Expand Down
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ check_PROGRAMS = \
tests/test-output-multi-polygon \
tests/test-output-multi-poly-trivial \
tests/test-output-pgsql \
tests/test-output-pgsql-z_order \
tests/test-output-pgsql-tablespace \
tests/test-pgsql-escape \
tests/test-parse-options \
Expand Down Expand Up @@ -90,6 +91,8 @@ tests_test_output_pgsql_SOURCES = tests/test-output-pgsql.cpp tests/common-pg.cp
tests_test_output_pgsql_LDADD = libosm2pgsql.la
tests_test_output_pgsql_tablespace_SOURCES = tests/test-output-pgsql-tablespace.cpp tests/common-pg.cpp
tests_test_output_pgsql_tablespace_LDADD = libosm2pgsql.la
tests_test_output_pgsql_z_order_SOURCES = tests/test-output-pgsql-z_order.cpp tests/common-pg.cpp
tests_test_output_pgsql_z_order_LDADD = libosm2pgsql.la
tests_test_pgsql_escape_SOURCES = tests/test-pgsql-escape.cpp
tests_test_pgsql_escape_LDADD = libosm2pgsql.la
tests_test_parse_options_SOURCES = tests/test-parse-options.cpp
Expand Down Expand Up @@ -155,6 +158,7 @@ tests_test_output_multi_polygon_LDADD += $(GLOBAL_LDFLAGS)
tests_test_output_multi_poly_trivial_LDADD += $(GLOBAL_LDFLAGS)
tests_test_output_pgsql_LDADD += $(GLOBAL_LDFLAGS)
tests_test_output_pgsql_tablespace_LDADD += $(GLOBAL_LDFLAGS)
tests_test_output_pgsql_z_order_LDADD += $(GLOBAL_LDFLAGS)
tests_test_pgsql_escape_LDADD += $(GLOBAL_LDFLAGS)
tests_test_parse_options_LDADD += $(GLOBAL_LDFLAGS)
tests_test_expire_tiles_LDADD += $(GLOBAL_LDFLAGS)
Expand Down
9 changes: 9 additions & 0 deletions docs/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Some osm2pgsql changes have slightly changed the database schema it expects. If
updating an old database, a migration may be needed. The migrations here assume
the default `planet_osm` prefix.

## 0.87.5-dev z_order changes ##

0.87.5-dev z_order code was changed. To migrate to the new z_order numbering run

```sql
UPDATE planet_osm_line SET z_order = z_order * 10;
UPDATE planet_osm_roads SET z_order = z_order * 10;
```

## 0.87.0 pending removal ##

0.87.0 moved the in-database tracking of pending ways and relations to
Expand Down
48 changes: 29 additions & 19 deletions tagtransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,31 @@ static const struct {
const char *highway;
int roads;
} layers[] = {
{ 3, "minor", 0 },
{ 3, "road", 0 },
{ 3, "unclassified", 0 },
{ 3, "residential", 0 },
{ 4, "tertiary_link", 0 },
{ 4, "tertiary", 0 },
{ 6, "secondary_link",1 },
{ 6, "secondary", 1 },
{ 7, "primary_link", 1 },
{ 7, "primary", 1 },
{ 8, "trunk_link", 1 },
{ 8, "trunk", 1 },
{ 9, "motorway_link", 1 },
{ 9, "motorway", 1 }
{ 10, "steps", 0 },
{ 10, "cycleway", 0 },
{ 10, "bridleway", 0 },
{ 10, "footway", 0 },
{ 10, "path", 0 },
{ 11, "track", 0 },
{ 15, "service", 0 },

{ 24, "tertiary_link", 0 },
{ 25, "secondary_link",1 },
{ 27, "primary_link", 1 },
{ 28, "trunk_link", 1 },
{ 29, "motorway_link", 1 },

{ 30, "raceway", 0 },
{ 31, "pedestrian", 0 },
{ 32, "living_street", 0 },
{ 33, "road", 0 },
{ 33, "unclassified", 0 },
{ 33, "residential", 0 },
{ 34, "tertiary", 0 },
{ 36, "secondary", 1 },
{ 37, "primary", 1 },
{ 38, "trunk", 1 },
{ 39, "motorway", 1 }
};

static const unsigned int nLayers = (sizeof(layers)/sizeof(*layers));
Expand All @@ -56,12 +67,11 @@ void add_z_order(taglist_t &tags, int *roads)
int z_order = 0;

int l = layer ? strtol(layer->c_str(), NULL, 10) : 0;
z_order = 10 * l;
z_order = 100 * l;
*roads = 0;

if (highway) {
for (unsigned i = 0; i < nLayers; i++) {
//if (layers[i].highway == *highway) {
if (!strcmp(layers[i].highway, highway->c_str())) {
z_order += layers[i].offset;
*roads = layers[i].roads;
Expand All @@ -71,18 +81,18 @@ void add_z_order(taglist_t &tags, int *roads)
}

if (railway && !railway->empty()) {
z_order += 5;
z_order += 35;
*roads = 1;
}
/* Administrative boundaries are rendered at low zooms so we prefer to use the roads table */
if (boundary && *boundary == "administrative")
*roads = 1;

if (bridge)
z_order += 10;
z_order += 100;

if (tunnel)
z_order -= 10;
z_order -= 100;

char z[13];
snprintf(z, sizeof(z), "%d", z_order);
Expand Down
186 changes: 186 additions & 0 deletions tests/test-output-pgsql-z_order.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cassert>
#include <sstream>
#include <stdexcept>
#include <memory>

#include "osmtypes.hpp"
#include "osmdata.hpp"
#include "output-pgsql.hpp"
#include "options.hpp"
#include "middle-pgsql.hpp"
#include "middle-ram.hpp"
#include "taginfo_impl.hpp"
#include "parse.hpp"

#include <libpq-fe.h>
#include <sys/types.h>
#include <unistd.h>

#include <boost/scoped_ptr.hpp>
#include <boost/lexical_cast.hpp>

#include "tests/middle-tests.hpp"
#include "tests/common-pg.hpp"

namespace {

struct skip_test : public std::exception {
const char *what() { return "Test skipped."; }
};

void run_test(const char* test_name, void (*testfunc)()) {
try {
fprintf(stderr, "%s\n", test_name);
testfunc();

} catch (const skip_test &) {
exit(77); // <-- code to skip this test.

} catch (const std::exception& e) {
fprintf(stderr, "%s\n", e.what());
fprintf(stderr, "FAIL\n");
exit(EXIT_FAILURE);
}

fprintf(stderr, "PASS\n");
}
#define RUN_TEST(x) run_test(#x, &(x))

void check_string(pg::conn_ptr &conn, std::string expected, const std::string &query) {
pg::result_ptr res = conn->exec(query);

int ntuples = PQntuples(res->get());
if (ntuples != 1) {
throw std::runtime_error((boost::format("Expected only one tuple from a query "
"to check a string, but got %1%. Query "
"was: %2%.")
% ntuples % query).str());
}

std::string actual = PQgetvalue(res->get(), 0, 0);

if (actual != expected) {
throw std::runtime_error((boost::format("Expected %1%, but got %2%, when running "
"query: %3%.")
% expected % actual % query).str());
}
}


void check_count(pg::conn_ptr &conn, int expected, const std::string &query) {
pg::result_ptr res = conn->exec(query);

int ntuples = PQntuples(res->get());
if (ntuples != 1) {
throw std::runtime_error((boost::format("Expected only one tuple from a query "
"to check COUNT(*), but got %1%. Query "
"was: %2%.")
% ntuples % query).str());
}

std::string numstr = PQgetvalue(res->get(), 0, 0);
int count = boost::lexical_cast<int>(numstr);

if (count != expected) {
throw std::runtime_error((boost::format("Expected %1%, but got %2%, when running "
"query: %3%.")
% expected % count % query).str());
}
}

void check_number(pg::conn_ptr &conn, double expected, const std::string &query) {
pg::result_ptr res = conn->exec(query);

int ntuples = PQntuples(res->get());
if (ntuples != 1) {
throw std::runtime_error((boost::format("Expected only one tuple from a query, "
" but got %1%. Query was: %2%.")
% ntuples % query).str());
}

std::string numstr = PQgetvalue(res->get(), 0, 0);
double num = boost::lexical_cast<double>(numstr);

// floating point isn't exact, so allow a 0.01% difference
if ((num > 1.0001*expected) || (num < 0.9999*expected)) {
throw std::runtime_error((boost::format("Expected %1%, but got %2%, when running "
"query: %3%.")
% expected % num % query).str());
}
}

void assert_has_table(pg::conn_ptr &test_conn, const std::string &table_name) {
std::string query = (boost::format("select count(*) from pg_catalog.pg_class "
"where relname = '%1%'")
% table_name).str();

check_count(test_conn, 1, query);
}

// "simple" test modeled on the basic regression test from
// the python script. this is just to check everything is
// working as expected before we start the complex stuff.
void test_z_order() {
boost::scoped_ptr<pg::tempdb> db;

try {
db.reset(new pg::tempdb);
} catch (const std::exception &e) {
std::cerr << "Unable to setup database: " << e.what() << "\n";
throw skip_test();
}

std::string proc_name("test-output-pgsql"), input_file("-");
char *argv[] = { &proc_name[0], &input_file[0], NULL };

boost::shared_ptr<middle_pgsql_t> mid_pgsql(new middle_pgsql_t());
options_t options = options_t::parse(2, argv);
options.conninfo = db->conninfo().c_str();
options.num_procs = 1;
options.prefix = "osm2pgsql_test";
options.style = "default.style";

boost::shared_ptr<output_pgsql_t> out_test(new output_pgsql_t(mid_pgsql.get(), options));

osmdata_t osmdata(mid_pgsql, out_test);

boost::scoped_ptr<parse_delegate_t> parser(new parse_delegate_t(options.extra_attributes, options.bbox, options.projection));

osmdata.start();

if (parser->streamFile("libxml2", "tests/test_output_pgsql_z_order.osm", options.sanitize, &osmdata) != 0) {
throw std::runtime_error("Unable to read input file `tests/test_output_pgsql_z_order.osm'.");
}

parser.reset(NULL);

osmdata.stop();

// start a new connection to run tests on
pg::conn_ptr test_conn = pg::conn::connect(db->conninfo());

assert_has_table(test_conn, "osm2pgsql_test_point");
assert_has_table(test_conn, "osm2pgsql_test_line");
assert_has_table(test_conn, "osm2pgsql_test_polygon");
assert_has_table(test_conn, "osm2pgsql_test_roads");

check_string(test_conn, "motorway", "SELECT highway FROM osm2pgsql_test_line WHERE layer IS NULL ORDER BY z_order DESC LIMIT 1 OFFSET 0");
check_string(test_conn, "trunk", "SELECT highway FROM osm2pgsql_test_line WHERE layer IS NULL ORDER BY z_order DESC LIMIT 1 OFFSET 1");
check_string(test_conn, "primary", "SELECT highway FROM osm2pgsql_test_line WHERE layer IS NULL ORDER BY z_order DESC LIMIT 1 OFFSET 2");
check_string(test_conn, "secondary", "SELECT highway FROM osm2pgsql_test_line WHERE layer IS NULL ORDER BY z_order DESC LIMIT 1 OFFSET 3");
check_string(test_conn, "tertiary", "SELECT highway FROM osm2pgsql_test_line WHERE layer IS NULL ORDER BY z_order DESC LIMIT 1 OFFSET 4");

check_string(test_conn, "residential", "SELECT highway FROM osm2pgsql_test_line ORDER BY z_order DESC LIMIT 1 OFFSET 0");
}

} // anonymous namespace

int main(int argc, char *argv[]) {
RUN_TEST(test_z_order);

return 0;
}
75 changes: 75 additions & 0 deletions tests/test_output_pgsql_z_order.osm
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6' generator='hand'>
<node id='1' version='1' visible='true' lat='49' lon='-122.5'>
<tag k='highway' v='bus_stop' />
</node>
<node id='2' version='1' visible='true' lat='49.005' lon='-122.51'>
<tag k='highway' v='bus_stop' />
</node>
<node id='3' version='1' visible='true' lat='49.01' lon='-122.5' />
<!-- yes, all the ways have the same linestring -->
<way id='1' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='motorway' />
</way>
<way id='2' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='motorway_link' />
</way>
<way id='3' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='trunk' />
</way>
<way id='4' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='trunk_link' />
</way>
<way id='5' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='primary' />
</way>
<way id='6' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='primary_link' />
</way>
<way id='7' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='secondary' />
</way>
<way id='8' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='secondary_link' />
</way>
<way id='9' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='tertiary' />
</way>
<way id='10' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='tertiary_link' />
</way>
<way id='11' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<tag k='highway' v='residential' />
<tag k='layer' v='5' />
</way>
<!-- put *something* in the polygon table -->
<way id='1000' version='1' visible='true'>
<nd ref='1' />
<nd ref='2' />
<nd ref='3' />
<nd ref='1' />
<tag k='building' v='yes' />
</way>
</osm>

0 comments on commit a2f5b94

Please sign in to comment.