一步步DIY: OSM-Web服务器(四) 对万国语的地名进行翻译与检索

经过 前三篇的调试,已经有了一个完整的Map可以浏览,我们痛苦的世界范围数据下载、导入过程也结束了。要提醒一下的是,鉴于网速,不要下载 planetosm.lastest 文件,因为这个文件每周更新,万一一周下不完,就over了。

当然了,导入后,别忘了

  1. sudo touch /var/lib/mod_tile/planet-import-complete  
  2. sudo chown www-data /var/lib/mod_tile/planet-import-complete  

设置时间戳哦!

   导入后,只有中国、日本有些中文字符,其他国家都是鸟语,必须进行汉化。用PostgreSQL count 一下,name 字段不为null 的条目太多了,利用在线的翻译API似乎不现实。我们通过下载字典来进行本地自动匹配与翻译。字典在http://download.csdn.net/detail/goldenhawking/4556453, 导入后,含有17万个地名翻译的表如下面所示


由于place_name 里的地名有不规范的表示,比如括号中的曾用名、用逗号分隔的等效名等情况,不能直接把地名表与planet_osm_roads 、planet_osm_polygon 、planet_osm_line、planet_osm_point 四张表的name字段做 like 或者 = 的换算。同样,即使是做正则式的匹配,也要考虑到比如   XXXX 与 XX'XX (YYYY) 的情况,即原本地名已经包含阿拉伯语与英语两种语言的情况。

   为此,写一个程序,进行匹配,提前把地名进行标准化。其算法过程是:

   读取planet_osm_roads 、planet_osm_polygon 、planet_osm_line、planet_osm_point四张表里  name is not null 并多于1个字符的地名,进行简化,清除括号、非拉丁、斯拉夫字符,而后与经过规范化的 place_name 进行匹配。为了存储独立的中文字段,在四张表尾部追加了一个trans_name_chs的 text 字段,以便存储纯粹的中文地名,供搜索用。

  1. ALTER TABLE planet_osm_point ADD COLUMN trans_name_chs text;  
  2. ALTER TABLE planet_osm_line ADD COLUMN trans_name_chs text;  
  3. ALTER TABLE planet_osm_polygon ADD COLUMN trans_name_chs text;  
  4. ALTER TABLE planet_osm_roads ADD COLUMN trans_name_chs text;  


 算法伪代码表示:

  

  1. void Match(unicode TableName)  
  2. {  
  3.     for_each (record in TableName where 长度>3)  
  4.     {  
  5.         unicode 地名 = record->name;  
  6.         //清除首尾空格  
  7.         TrimSpaces(地名);  
  8.   
  9.         //只保留两类字符,根据字符的unicode取值范围筛选  
  10.         unicode 词干 = FilterChar (地名, new LanguageFilter({拉丁,斯拉夫}));  
  11.   
  12.         //在翻译表中查找可能的翻译  
  13.             unicodeList 可能结果集 = DatabaseSearch("规范化词干表","like %s%",词干);  
  14.            
  15.         //对所有含有词干的可能结果,进行相似度排序,这里的策略是看看长度比例因子,  
  16.         //比如  Shanghai 与  Shanghai City 为 8:13, 与Shanghai 为 1:1 ,因此取 Shanghai  
  17.   
  18.         unicode 最佳解=null;  
  19.         double  最佳因子=0;  
  20.   
  21.             for_each (unicode 可能解 in 可能结果集 where length(词干)/length(可能解)>0.6)  
  22.         {  
  23.             double 当前因子 = length(词干)/length(可能解);  
  24.             if (当前因子>最佳因子)  
  25.             {  
  26.                 最佳解 = 可能解;  
  27.                 最佳因子 = 当前因子;      
  28.                 if (最佳因子 == 1)  
  29.                     break;  
  30.             }  
  31.         }  
  32.       
  33.         //刷新数据库  
  34.         if (最佳因子 >0)  
  35.         {  
  36.             unicode 翻译结果 = 最佳解 + "(" + 地名 + ")";  
  37.             UpdateTable(TableName, record->id, 翻译结果);  
  38.         }  
  39.     }     
  40. }  

匹配过程大概需要1-2天,匹配完成后,翻译好的地名便存入了name字段中。渲染瓦片,看一看,主要的地名都OK啦

德国的


西班牙的

非洲的

还有老美的


最后,为这些字段建立索引
  1. CREATE INDEX idx_planet_osm_roads_name ON planet_osm_roads USING btree ("name"where name is not null;  
  2. CREATE INDEX idx_planet_osm_roads_trans_name_chs ON planet_osm_roads USING btree ("trans_name_chs"where trans_name_chs is not null;  
  3. CREATE INDEX idx_planet_osm_polygon_name ON planet_osm_polygon USING btree ("name"where name is not null;  
  4. CREATE INDEX idx_planet_osm_polygon_trans_name_chs ON planet_osm_polygon USING btree ("trans_name_chs"where trans_name_chs is not null;  
  5. CREATE INDEX idx_planet_osm_line_name ON planet_osm_line USING btree ("name"where name is not null;  
  6. CREATE INDEX idx_planet_osm_line_trans_name_chs ON planet_osm_line USING btree ("trans_name_chs"where trans_name_chs is not null;  
  7. CREATE INDEX idx_planet_osm_point_name ON planet_osm_point USING btree ("name"where name is not null;  
  8. CREATE INDEX idx_planet_osm_point_trans_name_chs ON planet_osm_point USING btree ("trans_name_chs"where trans_name_chs is not null;  

全部搞定后,vacuum 一下,索引就可以立刻参与查询了,而且使用 FCGI 实现地名的检索就变得简单了。这里为了测试,直接用C写CGI程序。

程序实现两个功能,一个是根据地名检索旁边的GIS对象,另一个是根据坐标检索最近的地名。这里用到 PostGIS的 CoverBy 系列函数.  CGI代码:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <math.h>  
  4. #include <stdio.h>  
  5. #include <string.h>  
  6. #include <unistd.h>  
  7. #include <postgresql/libpq-fe.h>  
  8. #include <sys/stat.h>  
  9. const char * pConnDBStr = "host='localhost' dbname='osmgis' user='用户名' password='密码'";  
  10. #define FCGI_PRG 1  
  11.   
  12. #if FCGI_PRG != 0  
  13. #include <fcgi_stdio.h>  
  14. #endif  
  15.   
  16. const char * sql_mask = "\  
  17. select * from(\  
  18. select name , trans_name_chs ,ST_Distance(way,ST_Transform(ST_SetSRID(ST_Point(%lf,%lf),4326),900913)) as mindis,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id\n\  
  19.     from planet_osm_polygon\n\  
  20.     where\n\  
  21.         name is not null\n\  
  22.         and\n\  
  23.         way && ST_Transform(ST_SetSRID('BOX3D(%lf %lf,%lf %lf)'::box3d,4326),900913)\n\  
  24.  union\n\  
  25.  select name , trans_name_chs,ST_Distance(way,ST_Transform(ST_SetSRID(ST_Point(%lf,%lf),4326),900913)) as mindis,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id\n\  
  26.     from planet_osm_roads\n\  
  27.     where\n\  
  28.         name is not null\n\  
  29.         and\n\  
  30.         way && ST_Transform(ST_SetSRID('BOX3D(%lf %lf,%lf %lf)'::box3d,4326),900913)\n\  
  31.  union\n\  
  32.  select name, trans_name_chs,ST_Distance(way,ST_Transform(ST_SetSRID(ST_Point(%lf,%lf),4326),900913)) as mindis,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id\n\  
  33.     from planet_osm_point\n\  
  34.     where\n\  
  35.         name is not null\n\  
  36.         and\n\  
  37.         way && ST_Transform(ST_SetSRID('BOX3D(%lf %lf,%lf %lf)'::box3d,4326),900913)\n\  
  38.  union\n\  
  39.  select name , trans_name_chs,ST_Distance(way,ST_Transform(ST_SetSRID(ST_Point(%lf,%lf),4326),900913)) as mindis,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id\n\  
  40.     from planet_osm_line\  
  41.     where\  
  42.         name is not null\  
  43.         and\  
  44.         way && ST_Transform(ST_SetSRID('BOX3D(%lf %lf,%lf %lf)'::box3d,4326),900913)\  
  45. ) as full_objs\  
  46.  where mindis<200\  
  47.  order by mindis\  
  48.  limit 256\  
  49. ";  
  50.   
  51. const char * psql_findbyname =  
  52. "select distinct * from(\  
  53.     select name,trans_name_chs,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id from planet_osm_point where name like '%s%%' or trans_name_chs like '%s%%'\  
  54.     union\  
  55.     select name,trans_name_chs,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id from planet_osm_roads where name like '%s%%' or trans_name_chs like '%s%%'\  
  56.     union\  
  57.     select name,trans_name_chs,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id from planet_osm_line where name like '%s%%' or trans_name_chs like '%s%%'\  
  58.     union\  
  59.     select name,trans_name_chs,ST_AsText(ST_Transform(way,4326)) as geobj,osm_id from planet_osm_polygon where name like '%s%%' or trans_name_chs like '%s%%'\  
  60. ) as geobjs limit 256;";  
  61.   
  62. void initialize(void)  
  63. {  
  64. }  
  65.   
  66. //很据坐标检索地名  
  67. bool findobjs(double dCenterX,double dCenterY)  
  68. {  
  69.     bool succeeded = false;  
  70.   
  71.     double dLeft, dRight, dTop, dBottom;  
  72.   
  73.     dLeft = dCenterX - 0.1;  
  74.     dRight = dCenterX + 0.1;  
  75.     dTop = dCenterY+0.1;  
  76.     dBottom = dCenterY-0.1;  
  77.   
  78.     if (dLeft>=179 || dLeft <=-179 || dRight>=179 || dRight <=-179 ||  
  79.     fabs(dRight-dLeft)>0.5)  
  80.         return false;  
  81.   
  82.     if (dTop>=85 || dTop <=-85 || dBottom>=85 || dBottom <=-85 ||  
  83.     fabs(dTop-dBottom)>0.5)  
  84.         return false;  
  85.   
  86.   
  87.     PGconn * pConn = PQconnectdb(pConnDBStr);  
  88.     if (!pConn)  
  89.         printf("<p>Error ! Could not open PostgreSQL Connections.</p>\r\n");  
  90.     else if (PQstatus(pConn)==CONNECTION_OK)  
  91.     {  
  92.                 char bufsql[4096];  
  93.                 sprintf(bufsql,sql_mask,  
  94.                     dCenterX,dCenterY,dLeft,dBottom,dRight,dTop,  
  95.                     dCenterX,dCenterY,dLeft,dBottom,dRight,dTop,  
  96.                     dCenterX,dCenterY,dLeft,dBottom,dRight,dTop,  
  97.                     dCenterX,dCenterY,dLeft,dBottom,dRight,dTop);  
  98.                 //printf("<p>Executing %s</p>\r\n",bufsql);  
  99.                 PGresult * pRes = PQexec(pConn,bufsql);  
  100.                 if (!pRes)  
  101.                     printf("<p>Error!Cold not init selection recordset.%s</p>\r\n",PQerrorMessage(pConn));  
  102.                 else  
  103.                 {  
  104.                     if (PQresultStatus(pRes)==PGRES_TUPLES_OK)  
  105.                     {  
  106.                         int n=PQntuples(pRes);  
  107.                         if (n==0)  
  108.                         {  
  109.                             printf("<p>No symbols found.</p>\r\n");  
  110.                             succeeded = true;  
  111.                         }  
  112.                         else  
  113.                         {  
  114.                             //fetching results.  
  115.                             printf("<p>nItems = %d</p>\r\n<table border=1>",n);  
  116.                              printf("<tr><td>No.</td><td>name</td><td>trans_name</td><td>distance</td><td>geobj</td><td>geo_id</td></tr>");  
  117.                             for (int i=0;i<n;i++)  
  118.                             {  
  119.                                 printf("    <tr><td>%d</td>\r\n",i);  
  120.                                 for (int j=0;j<5;j++)  
  121.                                 {  
  122.                                     char * pstrVal = PQgetvalue(pRes,i,j);  
  123.                                     char buf_text[2048];  
  124.                                     if (pstrVal)  
  125.                                     {  
  126.                                         if (strlen(pstrVal)>1024)  
  127.                                         {  
  128.                                             strncpy(buf_text,pstrVal,1024);  
  129.                                             strcat(buf_text,"...");  
  130.                                         }  
  131.                                         else  
  132.                                             strcpy(buf_text,pstrVal);  
  133.                                         printf("        <td>%s</td>\r\n",buf_text);  
  134.                                     }  
  135.                                 }  
  136.                                 printf("</tr>\r\n");  
  137.                             }  
  138.                             printf("</table>\r\n");  
  139.                             succeeded = true;  
  140.   
  141.                         }  
  142.                     }  
  143.                     else  
  144.                         printf("<p>Error!Requesting Failed.%s</p>\r\n",PQresultErrorMessage(pRes));  
  145.                     PQclear (pRes);  
  146.                 }  
  147.   
  148.    }  
  149.     else  
  150.         printf("<p>Error!Connection Failed.%s</p>\r\n",PQerrorMessage(pConn));  
  151.   
  152.     if (pConn)  
  153.         PQfinish (pConn);  
  154.     return succeeded;  
  155. }  
  156. //根据地名检索坐标  
  157. bool findobjs(const char * name)  
  158. {  
  159.     bool succeeded = false;  
  160.   
  161.     if (!name)  
  162.         return succeeded;  
  163.     int nLen_name = strlen(name);  
  164.     bool failed = false;  
  165.     for (int i=0;i<nLen_name && failed==false;i++)  
  166.     {  
  167.         const char * pMask = "~!@#$%^&*()_+=-`{}[]:;'\"|\\<>/?";  
  168.         const char * pfinded = strchr(pMask,name[i]);  
  169.         if (pfinded>=pMask && pfinded<=pMask+strlen(pMask)-1)  
  170.             failed = true;  
  171.     }  
  172.   
  173.     printf("<p>Find Placename: %s</p>\r\n",name);  
  174.     if (failed==true)  
  175.     {  
  176.         printf("<p>Name \"%s\" has invalid charactors. Request rejected.</p>\r\n",name);  
  177.         return false;  
  178.     }  
  179.   
  180.     PGconn * pConn = PQconnectdb(pConnDBStr);  
  181.   
  182.     if (!pConn)  
  183.         printf("<p>Error ! Could not open PostgreSQL Connections.</p>\r\n");  
  184.     else if (PQstatus(pConn)==CONNECTION_OK)  
  185.     {  
  186.                 char bufsql[4096];  
  187.                 sprintf(bufsql,psql_findbyname,  
  188.                     name,name,name,name,name,name,name,name);  
  189.                 //printf("<p>Executing %s</p>\r\n",bufsql);  
  190.                 PGresult * pRes = PQexec(pConn,bufsql);  
  191.                 if (!pRes)  
  192.                     printf("<p>Error!Cold not init selection recordset.%s</p>\r\n",PQerrorMessage(pConn));  
  193.                 else  
  194.                 {  
  195.                     if (PQresultStatus(pRes)==PGRES_TUPLES_OK)  
  196.                     {  
  197.                         int n=PQntuples(pRes);  
  198.                         if (n==0)  
  199.                         {  
  200.                             printf("<p>No symbols found.</p>\r\n");  
  201.                             succeeded = true;  
  202.                         }  
  203.                         else  
  204.                         {  
  205.                             //fetching results.  
  206.                             printf("<p>nItems = %d</p>\r\n<table border=1>",n);  
  207.                             printf("<tr><td>num</td><td>name</td><td>trans_name</td><td>geobj</td><td>geo_id</td></tr>");  
  208.                             for (int i=0;i<n;i++)  
  209.                             {  
  210.                                 printf("<tr><td>%d</td>",i);  
  211.                                 {  
  212.                                     char * pstrVal = PQgetvalue(pRes,i,0);  
  213.                                     if (pstrVal)  
  214.                                         printf("<td>%s</td>",pstrVal);  
  215.                                 }  
  216.                                 {  
  217.                                     char * pstrVal = PQgetvalue(pRes,i,1);  
  218.                                     if (pstrVal)  
  219.                                         printf("<td>%s</td>",pstrVal);  
  220.                                 }  
  221.                                 {  
  222.                                     char * pstrVal = PQgetvalue(pRes,i,2);  
  223.                                     char buf_text[2048];  
  224.                                     if (pstrVal)  
  225.                                     {  
  226.                                         if (strlen(pstrVal)>1024)  
  227.                                         {  
  228.                                             strncpy(buf_text,pstrVal,1024);  
  229.                                             strcat(buf_text,"...");  
  230.                                         }  
  231.                                         else  
  232.                                             strcpy(buf_text,pstrVal);  
  233.                                         printf("        <td>%s</td>\r\n",buf_text);  
  234.                                     }  
  235.                                 }  
  236.                                  {  
  237.                                     char * pstrVal = PQgetvalue(pRes,i,3);  
  238.                                     if (pstrVal)  
  239.                                         printf("<td>%s</td>",pstrVal);  
  240.                                 }  
  241.                                 printf("</tr>\r\n");  
  242.                             }  
  243.                             succeeded = true;  
  244.                             printf("</table>\r\n");  
  245.   
  246.                         }  
  247.                     }  
  248.                     else  
  249.                         printf("<p>Error!Requesting Failed.%s</p>\r\n",PQresultErrorMessage(pRes));  
  250.                     PQclear (pRes);  
  251.                 }  
  252.   
  253.    }  
  254.     else  
  255.         printf("<p>Error!Connection Failed.%s</p>\r\n",PQerrorMessage(pConn));  
  256.   
  257.     if (pConn)  
  258.         PQfinish (pConn);  
  259.     return succeeded;  
  260. }  
  261.   
  262. int main(void)  
  263.   
  264. {  
  265.   
  266.     initialize();  
  267.   
  268. #if FCGI_PRG != 0  
  269.   
  270.     while (FCGI_Accept() >= 0)  
  271.     {  
  272.         char * rawdata = getenv("QUERY_STRING");  
  273.         //char * server_name = getenv("SERVER_NAME");  
  274.         double  cenx=0,ceny = 0;  
  275.   
  276.         printf("Content-type: text/html; charset=UTF-8\r\n"  
  277.             "\r\n"  
  278.             "<title>GEO Results</title>\r\n");  
  279.   
  280.         if (rawdata)  
  281.         {  
  282.             int nLen = strlen(rawdata);  
  283.             char * data = new char [nLen+1];  
  284.             strcpy (data,rawdata);  
  285.             char * pcenx = 0,* pceny = 0, * pname=0;  
  286.             char * tmp = 0;  
  287.   
  288.             tmp = strstr(data,"cenx=");  
  289.             if (tmp)  
  290.                 pcenx = tmp;  
  291.             tmp = strstr(data,"ceny=");  
  292.             if (tmp)  
  293.                 pceny = tmp;  
  294.             tmp = strstr(data,"name=");  
  295.             if (tmp)  
  296.                 pname = tmp;  
  297.             if ( pcenx && pceny)  
  298.             {  
  299.                 for (int i=0;i<nLen;i++)  
  300.                     if (data[i]=='?'||data[i]=='&')  
  301.                         data[i] = 0;  
  302.                 cenx =  atof(pcenx+5);  
  303.                 ceny =  atof(pceny+5);  
  304.                 delete [] data;  
  305.                 if (false==findobjs(cenx,ceny))  
  306.                 {  
  307.                     printf("<p>Error!DBoper Failed!</p><p>\r\n");  
  308.                     continue;  
  309.                 }  
  310.            }  
  311.             else if (pname)  
  312.             {  
  313.                 unsigned char bufCodes[1024];  
  314.                 unsigned char * pCode = (unsigned char *)pname+5;  
  315.                 int nCodes = 0;  
  316.                 if (*pCode=='%')  
  317.                 {  
  318.                     while (*pCode=='%'&&nCodes<1024-1)  
  319.                     {  
  320.                         if (pCode[1]==0)  
  321.                             break;  
  322.                         if (pCode[2]==0)  
  323.                             break;  
  324.                         if (pCode[1]>='0' && pCode[1]<='9')  
  325.                             bufCodes[nCodes] = pCode[1]-'0';  
  326.                         else if (pCode[1]>='A' && pCode[1]<='F')  
  327.                             bufCodes[nCodes] = pCode[1]-'A'+10;  
  328.                         else  
  329.                             break;  
  330.   
  331.                         bufCodes[nCodes] <<=4;  
  332.   
  333.                         if (pCode[2]>='0' && pCode[2]<='9')  
  334.                             bufCodes[nCodes] += pCode[2]-'0';  
  335.                         else if (pCode[2]>='A' && pCode[2]<='F')  
  336.                             bufCodes[nCodes] += pCode[2]-'A'+10;  
  337.                         else  
  338.                             break;  
  339.                         nCodes++;  
  340.                         pCode+=3;  
  341.                     }  
  342.                     bufCodes[nCodes]=0;  
  343.                 }  
  344.                 else  
  345.                     strcpy((char *)bufCodes,(char *)pCode);  
  346.                 if (bufCodes[0])  
  347.                 {  
  348.                     if (false==findobjs((char *)bufCodes))  
  349.                     {  
  350.                         printf("<p>Error!DBoper Failed!</p><p>\r\n");  
  351.                         continue;  
  352.                     }  
  353.                 }  
  354.                 else  
  355.                      printf("<p>Error!Wrong query place names!</p><p>\r\n");  
  356.   
  357.             }  
  358.             else  
  359.             {  
  360.                 printf("<p>Error! At least Missing para cenx,ceny or name.</p>\r\n");  
  361.                 delete [] data;  
  362.                 continue;  
  363.             }  
  364.   
  365.         }  
  366.   
  367.   
  368.     }  
  369. #else  
  370.     //测试  
  371.     findobjs (121.538,31.221);  
  372. #endif  
  373.     return 0;  
  374. }  

这里的功能及其有限,也没有考虑到安全性。仅仅实现功能而已。



下一篇,我们来看看使用 GeoServer 显示叠加的矢量图层数据。
阅读更多

没有更多推荐了,返回首页