要解析kml文件,首先是必須要知道kml是什么铜邮,其次還要了解kml的語法,然后才能談得上解析它寨蹋,這些都會了之后松蒜,假如我們遇到了10多(M)的文件的時候,又該如何來解析呢已旧,下面我們就從這幾個方面來入手秸苗,詳細(xì)了解如何解析15w行的kml文件。
一运褪、kml是什么
根據(jù)百度百科惊楼,我們可以知道:
KML,是標(biāo)記語言(Keyhole Markup Language)的縮寫秸讹,最初由Keyhole公司開發(fā)檀咙,是一種基于XML 語法與格式的、用于描述和保存地理信息(如點璃诀、線弧可、圖像、多邊形和模型等)的編碼規(guī)范劣欢,可以被 Google Earth 和 Google Maps 識別并顯示棕诵。
上面說了這么一大堆,說白了就是一句話:kml就是一種xml格式的文件凿将。因此我們就可以使用PHP對xml文件的解析函數(shù)或者是類來解析它校套,現(xiàn)在我們只是知道了可以使用像simplexml_load_string()這樣的函數(shù)來解析kml文件,但是kml文件中數(shù)據(jù)的結(jié)構(gòu)到底是怎么樣的牧抵,我們一點也不了解搔确,所以我們要學(xué)習(xí)kml文件的語法。
說到kml語法的話灭忠,因為它被谷歌公司廣泛采用膳算,那我們就要會翻墻----免費翻墻。現(xiàn)在我們假定所有的閱讀的人弛作,都已經(jīng)知道如何翻墻了涕蜂。
二、kml的語法
翻墻之后直接輸入:KML Document 進(jìn)行搜索映琳,也可以點擊這個網(wǎng)址kml文檔机隙,可以大致做一個了解蜘拉。下面我們就需要看我們的kml文件有哪些東西,如下圖:
接著就是具體的樣式數(shù)據(jù)和經(jīng)緯度數(shù)據(jù)有鹿,如下圖:
現(xiàn)在我們對我們自己需要解析的數(shù)據(jù)格式已經(jīng)了解了旭旭,下面我們就來解析這個kml文件。終于到正題了葱跋。
三持寄、解析kml
我們可以自己封裝一個方法,只需要傳入文件名就可以解析kml文件稍味,先來代碼:
/**
* 解析kml文件返回一個解析后的數(shù)據(jù)
* @param $file
* @return array
*/
function parseKML($file)
{
// 獲得文件內(nèi)容
$xml = simplexml_load_file($file);
// 輸出KML數(shù)據(jù)數(shù)組
$result = array();
// 讀取document標(biāo)簽
$values = $xml->Document;
$floderArr = array();
foreach ($values->Folder as $folder) {
$floderArr[] = $folder;
}
// 讀取style標(biāo)簽
$styleArrs = array();
foreach ($values->Style as $style) {
$jsonStyle = xmlToArray($style);
$key = $jsonStyle["@attributes"]["id"];
$styleArrs[$key] = $jsonStyle;
}
// 分別獲得線和區(qū)域的數(shù)據(jù)
$placeMarksCache = array();
foreach ($floderArr as $key => $value) {
$name = (string)$value->name;
if ($name == 'Area Features') {
foreach ($value->Placemark as $placeMark) {
$placeMarksCache['area'][] = $placeMark;
}
} else {
foreach ($value->Placemark as $placeMark) {
$placeMarksCache['lines'][] = $placeMark;
}
}
}
// 循環(huán)輸出數(shù)據(jù)
foreach ($placeMarksCache as $k => $place){
// 獲取要輸出的點集
$placeMarkOutCache = array();
// 將點集read出來
foreach ($place as $placeMark) {
$styleCache = (string)$placeMark->styleUrl;
$styleCache = str_replace("#", "", $styleCache);
$styleCache = $styleArrs[$styleCache];
if (!$styleCache) {
$styleCache = "00000000";
}
// 獲取點集合
$strCache = $placeMark->Polygon;
if ($strCache) {
$styleCache = "#" . $styleCache["PolyStyle"]["color"];
$strCache = (string)$strCache->outerBoundaryIs->LinearRing->coordinates;
$strCache = explode("\n ", trim($strCache));
} else {
$styleCache = "#" . $styleCache["LineStyle"]["color"];
$strCache = (string)$placeMark->LineString->coordinates;
$strCache = explode("\n ", trim($strCache));
}
// 分割點集 作為數(shù)組進(jìn)行保存
foreach ($strCache as $sc) {
$args = explode(",", $sc);
$coords[] = array($args[0], $args[1], $args[2]);
}
// 將color 和 points 作為對象保存到result中
$result[$k][] = array("color" => $styleCache, "points" => $coords);
// 將這個數(shù)組清空
$coords = array();
}
}
// 最后返回集合點數(shù)據(jù)
return $result;
}
初步的代碼已經(jīng)完成油宜,但是這樣的解析方式在數(shù)據(jù)量少的時候還可以接受慎冤,但是當(dāng)數(shù)據(jù)量達(dá)到了15W行的時候粪薛,無論怎樣寫搏恤,都會出現(xiàn)了內(nèi)存溢出的問題熟空,由此就引出了息罗,采用分頁的方式來進(jìn)行解析kml文件的辦法。
四迈喉、 采用分頁的方式解析kml文件
思路:既然我們采用上面的這種方式會出現(xiàn)內(nèi)存溢出的錯誤挨摸,那么我們可以一次不要將數(shù)據(jù)全部都解析出來得运,而是采用先解析一部分,繼而循環(huán)解析的方式來解析kml文件非剃。
有了思路之后我們就需要進(jìn)行分析推沸,具體要怎樣才能實現(xiàn)坤学。
分析:
- 使用simplexml_load_file()函數(shù)來引入要解析的kml文件深浮;
- 計算要從哪個點開始讀确晌;
- 解析document和style以及folder標(biāo)簽雨让;
- 解析folder標(biāo)簽中的數(shù)據(jù)栖忠,判斷是這個folder是Area數(shù)據(jù)還是Line數(shù)據(jù)庵寞,而且這2部分所采用的標(biāo)簽的層級是不一樣的捐川,因此需要分開來解析古沥;
- 判斷是否要輸出數(shù)據(jù),如果不是盹沈,則計算數(shù)據(jù)的總量襟诸;
- 循環(huán)總的點集數(shù)組歌亲,獲得要輸出的點集數(shù)組。
- 循環(huán)輸出的點集數(shù)組惋鸥,解析數(shù)據(jù)悍缠;
- 解析時飞蚓,將對應(yīng)的點集的樣式也解析出來溅漾;
- 解析時著榴,更需要判斷要解析的數(shù)據(jù)是area數(shù)據(jù)還是line數(shù)據(jù)暮胧,對于不同的數(shù)據(jù)往衷,采用不同方式進(jìn)行分割炼绘,并將點集保存起來驮捍。
- 解析完畢之后,需要計算以哪種方式進(jìn)行輸出东且,這是因為這2個folder標(biāo)簽中的數(shù)據(jù)不是一樣多;
- 輸出數(shù)據(jù)珊泳。
代碼如下:
/**
* 按照區(qū)域來解析kml文件
* @param $file
* @param int $page
* @param int $size
* @param bool $outData
* @return array
*/
public function parseKMLByArea($file, $page = 0, $size = 2, $outData = true)
{
$xml = simplexml_load_file($file);
$result = array();
// 計算起始位置
if ($page < 0) {
$page = 0;
}
$start = $page * $size;
$values = $xml->Document;
$floderArr = array();
foreach ($values->Folder as $folder) {
$floderArr[] = $folder;
}
// 讀取樣式標(biāo)簽
$styleArrs = array();
foreach ($values->Style as $style) {
$jsonStyle = xmlToArray($style);
$key = $jsonStyle["@attributes"]["id"];
$styleArrs[$key] = $jsonStyle;
}
$placeMarksCache = array();
foreach ($floderArr as $key => $value) {
$name = (string)$value->name;
if ($name == 'Area Features') {
foreach ($value->Placemark as $placeMark) {
$placeMarksCache['area'][] = $placeMark;
}
} else {
foreach ($value->Placemark as $placeMark) {
$placeMarksCache['lines'][] = $placeMark;
}
}
}
// 判斷是否需要數(shù)據(jù)輸出數(shù)據(jù)
if($outData==true){
foreach ($placeMarksCache as $k => $place){
// 獲取要輸出的點集
$placeMarkOutCache = array();
for ($j = 0; $j < $size; $j++) {
// 判斷是否超出數(shù)組的長度
if ($start+$j>=count($place)) {
break;
} else {
$placeMarkOutCache[] = $place[$start + $j];
}
}
// 將點集read出來
foreach ($placeMarkOutCache as $placeMark) {
$styleCache = (string)$placeMark->styleUrl;
$styleCache = str_replace("#", "", $styleCache);
$styleCache = $styleArrs[$styleCache];
if (!$styleCache) {
$styleCache = "00000000";
}
// 獲取點集合
$strCache = $placeMark->Polygon;
if ($strCache) {
$styleCache = "#" . $styleCache["PolyStyle"]["color"];
$strCache = (string)$strCache->outerBoundaryIs->LinearRing->coordinates;
$strCache = explode("\n ", trim($strCache));
} else {
$styleCache = "#" . $styleCache["LineStyle"]["color"];
$strCache = (string)$placeMark->LineString->coordinates;
$strCache = explode("\n ", trim($strCache));
}
// 分割點集 作為數(shù)組進(jìn)行保存
foreach ($strCache as $sc) {
$args = explode(",", $sc);
//$coords[] = array($args[0], $args[1], $args[2]);
$coords[] = array($args[0]+0.0050, $args[1]-0.0030);
}
// 將color 和 points 作為對象保存到result中
$result[$k][] = array("color" => $styleCache, "points" => $coords);
// 將這個數(shù)組清空
$coords = array();
}
}
}
// 計算placeMark的總數(shù)
$areaNum = sizeof($placeMarksCache['area']);
$lineNum = sizeof($placeMarksCache['line']);
$placeMarksCacheCount = $areaNum > $lineNum ? $areaNum : $lineNum;
// 計算總頁數(shù)
$countCache=$placeMarksCacheCount/$size;
// 存在余數(shù)進(jìn)1
$countCache=ceil($countCache);
// 封裝返回數(shù)據(jù)
$resultData = array(
"data"=>$result,
"totalPage"=>$countCache,
"size"=>$size,
"currPage"=>$page
);
return $resultData;
}
當(dāng)然這是我個人的一種解析方法跨扮,肯定還有更好的解決辦法,請不吝賜教璃氢。最后一也,需要配置一張解析完畢之后的圖讼渊。