如何在PHP中查找KML文件中的元素

iaqfqrcu  于 2022-11-28  发布在  PHP
关注(0)|答案(2)|浏览(144)

我尝试在kml文件中查找所有元素<coordinates>,但并非所有元素都具有相同的结构
例如:

<Placemark>
    <Polygon>
        <outerBoundaryIs>
            <LinearRing>
                <coordinates>
                    -103.6705130315019, 18.9861834531002, 312.462181927998 
                    -103.5618913951496, 18.98673753736649, 827.0547547230755
                    -103.6101814498474, 19.21464825463783, 601.556189231858
                    -103.6705130315019, 18.9861834531002, 312.462181927998 
                </coordinates>
            </LinearRing>
        </outerBoundaryIs>
    </Polygon>
</Placemark>

而其他文件则具有其他结构,例如:

<Placemark>
    <MultiGeometry>
        <Polygon>
            <outerBoundaryIs>
                <LinearRing>
                    <coordinates>
                        -104.085929389248,19.3541278793555, 0 
                        -104.085635763744,19.3536293022551, 0
                        -104.087174259165,19.3527060222406, 0
                        -104.087310816944,19.3536755662883, 0
                        -104.085929389248,19.3541278793555,0 
                    </coordinates>
                </LinearRing>
            </outerBoundaryIs>
        </Polygon>
    </MultiGeometry>
</Placemark>

我需要得到所有的坐标忽略父元素,同样,一些文件,有一个或多个坐标元素
下一个代码运行,但只得到一个元素,

foreach( $xml->getDocNamespaces(TRUE) as $strPrefix => $strNamespace ) {
    if(strlen($strPrefix)==0) {
        $strPrefix="a"; //Assign an arbitrary namespace prefix.
    }
    $xml->registerXPathNamespace($strPrefix,$strNamespace);
}

$pieces = explode(" ", $xml->xpath("//a:coordinates")[0]);
foreach ($pieces as $coordinates) {
    $args     = explode(",", $coordinates);
    if (strlen($args[1]) != 0 ){
        $coordenadas .= '{"lat": ' . $args[1] . ', "lng": ' . $args[0] . '},';
    }
}

如果该文件有其他坐标元素,我不能得到它。

vql8enpb

vql8enpb1#

无需使用SimpleXML及其关联的类/方法(我从未使用过,因此无法提供任何指导),使用原生DOMDocumentDOMXPath即可轻松完成查找KML文件中所有坐标的任务

# This source file has multiple separate Placemarks, each with many coordinates
# and has several different associated namespaces.
$file='/files/kml/cta.kml';
$output=array();

# load the XML/KML file
libxml_use_internal_errors( true );
$dom=new DOMDocument;
$dom->validateOnParse=false;
$dom->strictErrorChecking=false;
$dom->recover=true;
$dom->load( $file );
libxml_clear_errors();

# load XPath and register default namespace
$xp=new DOMXPath( $dom );

# Find & register all namespaces
$expr='namespace::*';
$col=$xp->query( $expr );
if( $col && $col->length > 0 ){
    foreach( $col as $index => $node ){
        $xp->registerNameSpace( $node->localName, $node->nodeValue );
        # set a default... 
        $def=$node->localName;
    }
}

# choose the default namespace and create the basic query
# In **this** file the prefix is not important, this may not always be the case!
$expr=sprintf('//%s:coordinates', $def );

#Query the document to find matching nodes.
$col=$xp->query( $expr );
if( $col && $col->length > 0 ){
    foreach( $col as $node ){
        # each found node has a long list of coordinates/altitudes - create
        # an array by exploding on new line character.
        $lines=explode( PHP_EOL, $node->nodeValue );
        
        
        
        #iterate through found lines of coordinates and split into constituent pieces.
        foreach( $lines as $line ){
            # remove tabs and other control characters
            $line=preg_replace('@[\t\r]@', '', $line );
            
            # split the line at suitable point
            $line=preg_split( '@\n@', $line );
            
            # remove empty items
            $line=array_filter( $line );
            
            foreach( $line as $coordinate ){
                # does each coordinate have an altitude or not?
                $count=substr_count( $coordinate, ',' );
                
                if( $count==1 ){
                    
                    # Only Long & Lat per coordinate
                    list( $lng, $lat )=explode(',', $coordinate );
                    $output[]=array( 'lat'=>trim($lat), 'lng'=>trim($lng), 'Altitude'=>0 );
                    
                } elseif( $count==2 ) {
                    
                    # Long, Lat & Altitude per coordinate
                    list( $lng, $lat, $alt )=explode(',', $coordinate );
                    $output[]=array( 'lat'=>trim($lat), 'lng'=>trim($lng), 'Altitude'=>trim($alt) );
                }
            }
        }
    }
}

printf('<pre>%s</pre>',print_r($output,true));

其输出格式为:

Array
(
    [0] => Array
        (
            [lat] => 41.97881025520548
            [lng] => -87.89289951324463
            [Altitude] => 0
        )
    [1] => Array
        (
            [lat] => 41.97788506340239
            [lng] => -87.89184808731079
            [Altitude] => 0
        )
    [2] => Array
        (
            [lat] => 41.97762983571196
            [lng] => -87.89150476455688
            [Altitude] => 0
        )

我验证了这将返回所有坐标,并对结果和原始结果中的字符串进行了一些基本计数。

4zcjmb1e

4zcjmb1e2#

请勿尝试从XML文件中的前置词判断命名空间。命名空间的唯一识别码是URI。前置词注册是为了提高可读性。任何元素节点都可以包含命名空间定义。命名空间前置词可以变更,而且对于元素节点而言是选择性的。
解析器将节点名称解析为本地名称和名称空间URI。以下3个示例都可以读作{http://www.opengis.net/kml/2.2}kmlClark notation)。

  • <kml xmlns="http://www.opengis.net/kml/2.2">
  • <k:kml xmlns:k="http://www.opengis.net/kml/2.2">
  • <keyhole:kml xmlns:keyhole="http://www.opengis.net/kml/2.2">

因此,只需为已知的名称空间URI定义并注册自己的前缀。
基本示例:

const XMLNS_KML = "http://www.opengis.net/kml/2.2";

$kml = new SimpleXMLElement(getKMLString());
$kml->registerXpathNamespace('k', XMLNS_KML);

$coordinates = [];
foreach ($kml->xpath('//k:Placemark//k:coordinates') as $coordinates) {
    var_dump(trim($coordinates));
}

function getKMLString(): string {
  return <<<'XML'
<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
    <Polygon>
        <outerBoundaryIs>
            <LinearRing>
                <coordinates>
                    -103.6705130315019, 18.9861834531002, 312.462181927998 
                    -103.5618913951496, 18.98673753736649, 827.0547547230755
                    -103.6101814498474, 19.21464825463783, 601.556189231858
                    -103.6705130315019, 18.9861834531002, 312.462181927998 
                </coordinates>
            </LinearRing>
        </outerBoundaryIs>
    </Polygon>
</Placemark>
<Placemark>
    <MultiGeometry>
        <Polygon>
            <outerBoundaryIs>
                <LinearRing>
                    <coordinates>
                        -104.085929389248,19.3541278793555, 0 
                        -104.085635763744,19.3536293022551, 0
                        -104.087174259165,19.3527060222406, 0
                        -104.087310816944,19.3536755662883, 0
                        -104.085929389248,19.3541278793555,0 
                    </coordinates>
                </LinearRing>
            </outerBoundaryIs>
        </Polygon>
    </MultiGeometry>
</Placemark>
</kml>
XML;
}

输出量:

string(283) "-103.6705130315019, 18.9861834531002, 312.462181927998 
                    -103.5618913951496, 18.98673753736649, 827.0547547230755
                    -103.6101814498474, 19.21464825463783, 601.556189231858
                    -103.6705130315019, 18.9861834531002, 312.462181927998"
string(285) "-104.085929389248,19.3541278793555, 0 
                        -104.085635763744,19.3536293022551, 0
                        -104.087174259165,19.3527060222406, 0
                        -104.087310816944,19.3536755662883, 0
                        -104.085929389248,19.3541278793555,0"

重要!如果在返回的SimpleXMLElement示例上使用Xpath表达式,则需要在每个对象上再次注册。
使用DOM,引导程序稍微多一些,但你有一个显式的$xpath对象,带有名称空间注册。而且DOMXpath::evaluate()支持更多的表达式语法,可以直接返回标量值。

$document = new DOMDocument();
$document->loadXML(getKMLString());
$xpath = new DOMXPath($document);
$xpath->registerNamespace('k', XMLNS_KML);

$coordinates = [];
foreach ($xpath->evaluate('//k:Placemark//k:coordinates') as $coordinates) {
    var_dump(trim($coordinates->textContent));
}

地标坐标可以只是一个点、一个简单的形状或复杂的形状-请参阅https://developers.google.com/kml/documentation/kml_tut#placemarks。

相关问题