From: Sarah Hoffmann Date: Fri, 10 Apr 2020 21:09:16 +0000 (+0200) Subject: Merge remote-tracking branch 'upstream/master' X-Git-Tag: deploy~241 X-Git-Url: https://git.openstreetmap.org/nominatim.git/commitdiff_plain/6a4192d3d18e9db2dce7d8f9f1b229d5ae9f7c6f?hp=1c7da4318298a790e63c5648de57b3b804426731 Merge remote-tracking branch 'upstream/master' --- diff --git a/.travis.yml b/.travis.yml index 0ee38592..f5344742 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ --- -sudo: required +os: linux dist: xenial language: python python: diff --git a/lib/ClassTypes.php b/lib/ClassTypes.php index 91066a1f..60f18d95 100644 --- a/lib/ClassTypes.php +++ b/lib/ClassTypes.php @@ -79,12 +79,13 @@ function getList() 'boundary:administrative:1' => array('label' => 'Continent', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), 'boundary:administrative:2' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), 'place:country' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 6, 'defdiameter' => 15), - 'boundary:administrative:3' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), + 'boundary:administrative:3' => array('label' => 'Region', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), 'boundary:administrative:4' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), 'place:state' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 5.12), + 'place:province' => array('label' => 'Province', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 5.12), 'boundary:administrative:5' => array('label' => 'State District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), 'boundary:administrative:6' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), - 'boundary:administrative:7' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), + 'boundary:administrative:7' => array('label' => 'Municipality', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), 'place:county' => array('label' => 'County', 'frequency' => 108, 'icon' => 'poi_boundary_administrative', 'defzoom' => 10, 'defdiameter' => 1.28), 'boundary:administrative:8' => array('label' => 'City', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32), 'place:city' => array('label' => 'City', 'frequency' => 66, 'icon' => 'poi_place_city', 'defzoom' => 12, 'defdiameter' => 0.32), diff --git a/lib/setup/SetupClass.php b/lib/setup/SetupClass.php index aa1b291d..ac0f8f02 100755 --- a/lib/setup/SetupClass.php +++ b/lib/setup/SetupClass.php @@ -695,6 +695,7 @@ class SetupFunctions $sBasePath = CONST_BasePath.'/sql/functions/'; $sTemplate = file_get_contents($sBasePath.'utils.sql'); $sTemplate .= file_get_contents($sBasePath.'normalization.sql'); + $sTemplate .= file_get_contents($sBasePath.'ranking.sql'); $sTemplate .= file_get_contents($sBasePath.'importance.sql'); $sTemplate .= file_get_contents($sBasePath.'address_lookup.sql'); $sTemplate .= file_get_contents($sBasePath.'interpolation.sql'); diff --git a/lib/template/deletable-html.php b/lib/template/deletable-html.php new file mode 100644 index 00000000..8cc53e84 --- /dev/null +++ b/lib/template/deletable-html.php @@ -0,0 +1,48 @@ + + Nominatim Deleted Data + + + + +
+

Deletable

+

+ objects have been deleted in OSM but are still in the Nominatim database. + Also available in JSON format. +

+ + +'; + foreach (array_keys($aPolygons[0]) as $sCol) { + echo ''; + } + echo ''; + foreach ($aPolygons as $aRow) { + echo ''; + foreach ($aRow as $sCol => $sVal) { + switch ($sCol) { + case 'osm_id': + echo ''; + break; + case 'place_id': + echo ''; + break; + default: + echo ''; + break; + } + } + echo ''; + } +} +?> +
'.$sCol.'
'.osmLink($aRow).''.detailsLink($aRow).''.($sVal?$sVal:' ').'
+
+ + diff --git a/lib/template/polygons-html.php b/lib/template/polygons-html.php new file mode 100644 index 00000000..7d485d9c --- /dev/null +++ b/lib/template/polygons-html.php @@ -0,0 +1,71 @@ + + Nominatim Broken Polygon Data + + + + + +
+

Broken polygons

+ +

+ Total number of broken polygons: . + Also available in JSON format. +

+ + + +'; + //var_dump($aPolygons[0]); + foreach (array_keys($aPolygons[0]) as $sCol) { + echo ''; + } + echo ''; + echo ''; + $aSeen = array(); + foreach ($aPolygons as $aRow) { + if (isset($aSeen[$aRow['osm_type'].$aRow['osm_id']])) continue; + $aSeen[$aRow['osm_type'].$aRow['osm_id']] = 1; + + echo ''; + $sOSMType = formatOSMType($aRow['osm_type']); + foreach ($aRow as $sCol => $sVal) { + switch ($sCol) { + case 'errormessage': + if (preg_match('/Self-intersection\\[([0-9.\\-]+) ([0-9.\\-]+)\\]/', $sVal, $aMatch)) { + $aRow['lat'] = $aMatch[2]; + $aRow['lon'] = $aMatch[1]; + $sUrl = sprintf('https://www.openstreetmap.org/?lat=%f&lon=%f&zoom=18&layers=M&%s=%d', + $aRow['lat'], + $aRow['lon'], + $sOSMType, + $aRow['osm_id']); + echo ''; + } else { + echo ''; + } + break; + case 'osm_id': + echo ''; + break; + default: + echo ''; + break; + } + } + $sJosmUrl = 'http://localhost:8111/import?url=https://www.openstreetmap.org/api/0.6/'.$sOSMType.'/'.$aRow['osm_id'].'/full'; + echo ''; + echo ''; + } + echo '
'.$sCol.' 
'.($sVal?$sVal:' ').''.($sVal?$sVal:' ').''.osmLink(array('osm_type' => $aRow['osm_type'], 'osm_id' => $aRow['osm_id'])).''.($sVal?$sVal:' ').'josm
'; +} +?> +
+ + \ No newline at end of file diff --git a/sql/functions/placex_triggers.sql b/sql/functions/placex_triggers.sql index c04aef28..0151162e 100644 --- a/sql/functions/placex_triggers.sql +++ b/sql/functions/placex_triggers.sql @@ -1,45 +1,5 @@ -- Trigger functions for the placex table. -CREATE OR REPLACE FUNCTION get_rel_node_members(members TEXT[], memberLabels TEXT[]) - RETURNS SETOF BIGINT - AS $$ -DECLARE - i INTEGER; -BEGIN - FOR i IN 1..ARRAY_UPPER(members,1) BY 2 LOOP - IF members[i+1] = ANY(memberLabels) - AND upper(substring(members[i], 1, 1))::char(1) = 'N' - THEN - RETURN NEXT substring(members[i], 2)::bigint; - END IF; - END LOOP; - - RETURN; -END; -$$ -LANGUAGE plpgsql IMMUTABLE; - --- copy 'name' to or from the default language (if there is a default language) -CREATE OR REPLACE FUNCTION add_default_place_name(country_code VARCHAR(2), - INOUT name HSTORE) - AS $$ -DECLARE - default_language VARCHAR(10); -BEGIN - IF name is not null AND array_upper(akeys(name),1) > 1 THEN - default_language := get_country_language_code(country_code); - IF default_language IS NOT NULL THEN - IF name ? 'name' AND NOT name ? ('name:'||default_language) THEN - name := name || hstore(('name:'||default_language), (name -> 'name')); - ELSEIF name ? ('name:'||default_language) AND NOT name ? 'name' THEN - name := name || hstore('name', (name -> ('name:'||default_language))); - END IF; - END IF; - END IF; -END; -$$ -LANGUAGE plpgsql IMMUTABLE; - -- Find the parent road of a POI. -- -- \returns Place ID of parent object or NULL if none @@ -538,25 +498,7 @@ BEGIN END IF; ELSE -- mark nearby items for re-indexing, where 'nearby' depends on the features rank_search and is a complete guess :( - diameter := 0; - -- 16 = city, anything higher than city is effectively ignored (polygon required!) - IF NEW.type='postcode' THEN - diameter := 0.05; - ELSEIF NEW.rank_search < 16 THEN - diameter := 0; - ELSEIF NEW.rank_search < 18 THEN - diameter := 0.1; - ELSEIF NEW.rank_search < 20 THEN - diameter := 0.05; - ELSEIF NEW.rank_search = 21 THEN - diameter := 0.001; - ELSEIF NEW.rank_search < 24 THEN - diameter := 0.02; - ELSEIF NEW.rank_search < 26 THEN - diameter := 0.002; -- 100 to 200 meters - ELSEIF NEW.rank_search < 28 THEN - diameter := 0.001; -- 50 to 100 meters - END IF; + diameter := update_place_diameter(NEW.rank_search); IF diameter > 0 THEN -- RAISE WARNING 'placex point insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,diameter; IF NEW.rank_search >= 26 THEN diff --git a/sql/functions/ranking.sql b/sql/functions/ranking.sql new file mode 100644 index 00000000..ecd31e90 --- /dev/null +++ b/sql/functions/ranking.sql @@ -0,0 +1,116 @@ +-- Functions related to search and address ranks + +-- Return an approximate search radius according to the search rank. +CREATE OR REPLACE FUNCTION reverse_place_diameter(rank_search SMALLINT) + RETURNS FLOAT + AS $$ +BEGIN + IF rank_search <= 4 THEN + RETURN 5.0; + ELSIF rank_search <= 8 THEN + RETURN 1.8; + ELSIF rank_search <= 12 THEN + RETURN 0.6; + ELSIF rank_search <= 17 THEN + RETURN 0.16; + ELSIF rank_search <= 18 THEN + RETURN 0.08; + ELSIF rank_search <= 19 THEN + RETURN 0.04; + END IF; + + RETURN 0.02; +END; +$$ +LANGUAGE plpgsql IMMUTABLE; + + +-- Return an approximate update radius according to the search rank. +CREATE OR REPLACE FUNCTION update_place_diameter(rank_search SMALLINT) + RETURNS FLOAT + AS $$ +BEGIN + -- postcodes + IF rank_search = 11 or rank_search = 5 THEN + RETURN 0.05; + -- anything higher than city is effectively ignored (polygon required) + ELSIF rank_search < 16 THEN + RETURN 0; + ELSIF rank_search < 18 THEN + RETURN 0.1; + ELSIF rank_search < 20 THEN + RETURN 0.05; + ELSIF rank_search = 21 THEN + RETURN 0.001; + ELSIF rank_search < 24 THEN + RETURN 0.02; + ELSIF rank_search < 26 THEN + RETURN 0.002; + ELSIF rank_search < 28 THEN + RETURN 0.001; + END IF; + + RETURN 0; +END; +$$ +LANGUAGE plpgsql IMMUTABLE; + + +-- Guess a ranking for postcodes from country and postcode format. +CREATE OR REPLACE FUNCTION get_postcode_rank(country_code VARCHAR(2), postcode TEXT, + OUT rank_search SMALLINT, + OUT rank_address SMALLINT) +AS $$ +DECLARE + part TEXT; +BEGIN + rank_search := 30; + rank_address := 30; + postcode := upper(postcode); + + IF country_code = 'gb' THEN + IF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9][A-Z][A-Z])$' THEN + rank_search := 25; + rank_address := 5; + ELSEIF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9])$' THEN + rank_search := 23; + rank_address := 5; + ELSEIF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z])$' THEN + rank_search := 21; + rank_address := 5; + END IF; + + ELSEIF country_code = 'sg' THEN + IF postcode ~ '^([0-9]{6})$' THEN + rank_search := 25; + rank_address := 11; + END IF; + + ELSEIF country_code = 'de' THEN + IF postcode ~ '^([0-9]{5})$' THEN + rank_search := 21; + rank_address := 11; + END IF; + + ELSE + -- Guess at the postcode format and coverage (!) + IF postcode ~ '^[A-Z0-9]{1,5}$' THEN -- Probably too short to be very local + rank_search := 21; + rank_address := 11; + ELSE + -- Does it look splitable into and area and local code? + part := substring(postcode from '^([- :A-Z0-9]+)([- :][A-Z0-9]+)$'); + + IF part IS NOT NULL THEN + rank_search := 25; + rank_address := 11; + ELSEIF postcode ~ '^[- :A-Z0-9]{6,}$' THEN + rank_search := 21; + rank_address := 11; + END IF; + END IF; + END IF; + +END; +$$ +LANGUAGE plpgsql IMMUTABLE; diff --git a/sql/functions/utils.sql b/sql/functions/utils.sql index 6371e9ad..61033fb4 100644 --- a/sql/functions/utils.sql +++ b/sql/functions/utils.sql @@ -38,85 +38,58 @@ END; $$ LANGUAGE plpgsql IMMUTABLE; - -CREATE OR REPLACE FUNCTION reverse_place_diameter(rank_search SMALLINT) - RETURNS FLOAT +-- Return the node members with a given label from a relation member list +-- as a set. +-- +-- \param members Member list in osm2pgsql middle format. +-- \param memberLabels Array of labels to accept. +-- +-- \returns Set of OSM ids of nodes that are found. +-- +CREATE OR REPLACE FUNCTION get_rel_node_members(members TEXT[], + memberLabels TEXT[]) + RETURNS SETOF BIGINT AS $$ +DECLARE + i INTEGER; BEGIN - IF rank_search <= 4 THEN - RETURN 5.0; - ELSIF rank_search <= 8 THEN - RETURN 1.8; - ELSIF rank_search <= 12 THEN - RETURN 0.6; - ELSIF rank_search <= 17 THEN - RETURN 0.16; - ELSIF rank_search <= 18 THEN - RETURN 0.08; - ELSIF rank_search <= 19 THEN - RETURN 0.04; - END IF; + FOR i IN 1..ARRAY_UPPER(members,1) BY 2 LOOP + IF members[i+1] = ANY(memberLabels) + AND upper(substring(members[i], 1, 1))::char(1) = 'N' + THEN + RETURN NEXT substring(members[i], 2)::bigint; + END IF; + END LOOP; - RETURN 0.02; + RETURN; END; $$ LANGUAGE plpgsql IMMUTABLE; - -CREATE OR REPLACE FUNCTION get_postcode_rank(country_code VARCHAR(2), postcode TEXT, - OUT rank_search SMALLINT, - OUT rank_address SMALLINT) -AS $$ +-- Copy 'name' to or from the default language. +-- +-- \param country_code Country code of the object being named. +-- \param[inout] name List of names of the object. +-- +-- If the country named by country_code has a single default language, +-- then a `name` tag is copied to `name:` if this tag does +-- not yet exist and vice versa. +CREATE OR REPLACE FUNCTION add_default_place_name(country_code VARCHAR(2), + INOUT name HSTORE) + AS $$ DECLARE - part TEXT; + default_language VARCHAR(10); BEGIN - rank_search := 30; - rank_address := 30; - postcode := upper(postcode); - - IF country_code = 'gb' THEN - IF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9][A-Z][A-Z])$' THEN - rank_search := 25; - rank_address := 5; - ELSEIF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9])$' THEN - rank_search := 23; - rank_address := 5; - ELSEIF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z])$' THEN - rank_search := 21; - rank_address := 5; - END IF; - - ELSEIF country_code = 'sg' THEN - IF postcode ~ '^([0-9]{6})$' THEN - rank_search := 25; - rank_address := 11; - END IF; - - ELSEIF country_code = 'de' THEN - IF postcode ~ '^([0-9]{5})$' THEN - rank_search := 21; - rank_address := 11; - END IF; - - ELSE - -- Guess at the postcode format and coverage (!) - IF postcode ~ '^[A-Z0-9]{1,5}$' THEN -- Probably too short to be very local - rank_search := 21; - rank_address := 11; - ELSE - -- Does it look splitable into and area and local code? - part := substring(postcode from '^([- :A-Z0-9]+)([- :][A-Z0-9]+)$'); - - IF part IS NOT NULL THEN - rank_search := 25; - rank_address := 11; - ELSEIF postcode ~ '^[- :A-Z0-9]{6,}$' THEN - rank_search := 21; - rank_address := 11; - END IF; - END IF; + IF name is not null AND array_upper(akeys(name),1) > 1 THEN + default_language := get_country_language_code(country_code); + IF default_language IS NOT NULL THEN + IF name ? 'name' AND NOT name ? ('name:'||default_language) THEN + name := name || hstore(('name:'||default_language), (name -> 'name')); + ELSEIF name ? ('name:'||default_language) AND NOT name ? 'name' THEN + name := name || hstore('name', (name -> ('name:'||default_language))); + END IF; END IF; - + END IF; END; $$ LANGUAGE plpgsql IMMUTABLE; @@ -509,22 +482,7 @@ BEGIN AND rank_search > rank and indexed_status = 0 and ST_geometrytype(placex.geometry) != 'ST_Point' and (rank_search < 28 or name is not null or (rank >= 16 and address ? 'place')); END LOOP; ELSE - diameter := 0; - IF rank = 11 THEN - diameter := 0.05; - ELSEIF rank < 18 THEN - diameter := 0.1; - ELSEIF rank < 20 THEN - diameter := 0.05; - ELSEIF rank = 21 THEN - diameter := 0.001; - ELSEIF rank < 24 THEN - diameter := 0.02; - ELSEIF rank < 26 THEN - diameter := 0.002; -- 100 to 200 meters - ELSEIF rank < 28 THEN - diameter := 0.001; -- 50 to 100 meters - END IF; + diameter := update_place_diameter(rank); IF diameter > 0 THEN IF rank >= 26 THEN -- roads may cause reparenting for >27 rank places diff --git a/test/php/Nominatim/ClassTypesTest.php b/test/php/Nominatim/ClassTypesTest.php index 8d8481f5..cec3b82a 100644 --- a/test/php/Nominatim/ClassTypesTest.php +++ b/test/php/Nominatim/ClassTypesTest.php @@ -18,9 +18,9 @@ class ClassTypesTest extends \PHPUnit\Framework\TestCase 'rank_address' => 14 ); - $this->assertEquals('County', ClassTypes\getInfo($aPlace)['label']); - $this->assertEquals('County', ClassTypes\getFallbackInfo($aPlace)['label']); - $this->assertEquals('County', ClassTypes\getProperty($aPlace, 'label')); + $this->assertEquals('Municipality', ClassTypes\getInfo($aPlace)['label']); + $this->assertEquals('Municipality', ClassTypes\getFallbackInfo($aPlace)['label']); + $this->assertEquals('Municipality', ClassTypes\getProperty($aPlace, 'label')); // 2) No admin level // Eiffel Tower diff --git a/website/deletable.php b/website/deletable.php index ac4294ba..2d7ee7e9 100644 --- a/website/deletable.php +++ b/website/deletable.php @@ -5,7 +5,9 @@ require_once(CONST_BasePath.'/lib/log.php'); require_once(CONST_BasePath.'/lib/output.php'); ini_set('memory_limit', '200M'); -$sOutputFormat = 'html'; +$oParams = new Nominatim\ParameterParser(); +$sOutputFormat = $oParams->getSet('format', array('html', 'json'), 'html'); +set_exception_handler_by_format($sOutputFormat); $oDB = new Nominatim\DB(); $oDB->connect(); @@ -21,86 +23,8 @@ if (CONST_Debug) { exit; } -?> - - - - - - - Nominatim Deleted Data - - - - - - - - -

Objects in this table have been deleted in OSM but are still in the Nominatim database.

- - -'; -// var_dump($aPolygons[0]); -foreach ($aPolygons[0] as $sCol => $sVal) { - echo ''; +if ($sOutputFormat == 'json') { + echo javascript_renderData($aPolygons); +} else { + include(CONST_BasePath.'/lib/template/deletable-html.php'); } -echo ''; -foreach ($aPolygons as $aRow) { - echo ''; - foreach ($aRow as $sCol => $sVal) { - switch ($sCol) { - case 'osm_id': - echo ''; - break; - case 'place_id': - echo ''; - break; - default: - echo ''; - break; - } - } - echo ''; -} - -?> -
'.$sCol.'
'.osmLink($aRow).''.detailsLink($aRow).''.($sVal?$sVal:' ').'
- - - - - diff --git a/website/polygons.php b/website/polygons.php index 29fc5c27..c5fbafc2 100644 --- a/website/polygons.php +++ b/website/polygons.php @@ -6,8 +6,9 @@ require_once(CONST_BasePath.'/lib/output.php'); ini_set('memory_limit', '200M'); $oParams = new Nominatim\ParameterParser(); +$sOutputFormat = $oParams->getSet('format', array('html', 'json'), 'html'); +set_exception_handler_by_format($sOutputFormat); -$sOutputFormat = 'html'; $iDays = $oParams->getInt('days', false); $bReduced = $oParams->getBool('reduced', false); $sClass = $oParams->getString('class', false); @@ -15,13 +16,13 @@ $sClass = $oParams->getString('class', false); $oDB = new Nominatim\DB(); $oDB->connect(); -$iTotalBroken = (int) $oDB->getOne('select count(*) from import_polygon_error'); +$iTotalBroken = (int) $oDB->getOne('SELECT count(*) FROM import_polygon_error'); $aPolygons = array(); while ($iTotalBroken && empty($aPolygons)) { - $sSQL = 'select osm_type as "type",osm_id as "id",class as "key",type as "value",name->\'name\' as "name",'; - $sSQL .= 'country_code as "country",errormessage as "error message",updated'; - $sSQL .= ' from import_polygon_error'; + $sSQL = 'SELECT osm_type, osm_id, class, type, name->\'name\' as "name",'; + $sSQL .= 'country_code, errormessage, updated'; + $sSQL .= ' FROM import_polygon_error'; $aWhere = array(); if ($iDays) { @@ -33,10 +34,10 @@ while ($iTotalBroken && empty($aPolygons)) { if ($sClass) $sWhere[] = "class = '".pg_escape_string($sClass)."'"; if (!empty($aWhere)) { - $sSQL .= ' where '.join(' and ', $aWhere); + $sSQL .= ' WHERE '.join(' and ', $aWhere); } - $sSQL .= ' order by updated desc limit 1000'; + $sSQL .= ' ORDER BY updated desc LIMIT 1000'; $aPolygons = $oDB->getAll($sSQL); } @@ -45,93 +46,8 @@ if (CONST_Debug) { exit; } -?> - - - - - - - Nominatim Broken Polygon Data - - - - - - - - -Total number of broken polygons: $iTotalBroken

"; -if (!$aPolygons) exit; -echo ''; -echo ''; -//var_dump($aPolygons[0]); -foreach ($aPolygons[0] as $sCol => $sVal) { - echo ''; -} -echo ''; -echo ''; -$aSeen = array(); -foreach ($aPolygons as $aRow) { - if (isset($aSeen[$aRow['type'].$aRow['id']])) continue; - $aSeen[$aRow['type'].$aRow['id']] = 1; - echo ''; - foreach ($aRow as $sCol => $sVal) { - switch ($sCol) { - case 'error message': - if (preg_match('/Self-intersection\\[([0-9.\\-]+) ([0-9.\\-]+)\\]/', $sVal, $aMatch)) { - $aRow['lat'] = $aMatch[2]; - $aRow['lon'] = $aMatch[1]; - echo ''; - } else { - echo ''; - } - break; - case 'id': - echo ''; - break; - default: - echo ''; - break; - } - } - echo ''; - echo ''; -} -echo '
'.$sCol.' 
'.($sVal?$sVal:' ').''.($sVal?$sVal:' ').''.osmLink(array('osm_type' => $aRow['type'], 'osm_id' => $aRow['id'])).''.($sVal?$sVal:' ').'josm
'; - -?> - -