element', //internal name or comment
'Document', //KML tag name
array('name' => 'My kml Layer'), //'array of child tag' => 'value'
array(id' => 'documentId') //'tag attrib' => 'value'
);
$coords = '-116, -30, 0';
$pt = new KmlClass(
'#coordinates for my house',
'Point',
array(
'coordinates' => $coords,
'altitudeMode' => 'relativeToGround'
)
);
$pl = new KmlClass(
'#my house',
'Placemark',
array(
'name' => 'Home',
'Snippet' => '',
'visibility' => 0,
'styleUrl' => '#myStyle',
'Point' => $pt,
'description' => 'Here is the house I grew up in...')
);
$doc->appendChild( array('Placemark' => $pl) );
//You can also pass in an array of Points/KML
//tags if they are allowed to occur multiple times
//$doc->appendChild( array('Placemark' => array($pl, $pl2, $pl3)) );
$kml_output = '' . "\n";
$kml_output = '' . "\n";
generate_kml($kml_output, $doc); //Global funciton from kml_classX.php
$kml_output .= '';
echo $kml_output;
KML schema
*/
$Relationships = array (
'address' => array('CPX' => 0, 'VAL' => '.*'),
'AddressDetails' => array('CPX' => 1, 'VAL' => '.*'),
'altitude' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'altitudeMode' => array('CPX' => 0, 'VAL' => '(absolute|clampedToGround|relativeToGround)'),
'antialias' => array('CPX' => 0, 'VAL' => '(0|1)', 'UND' => 1),
'BalloonStyle' => array('CPX' => 1, 'OPT' => 'color,colorMode,text,textColor'),
'begin' => array('CPX' => 0, 'OPT' => 'TimeInstant'),
'Change' => array('CPX' => 1, 'VAL' => '.*'),
'Channel' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'NOTES' => 'has attributes: adwords, adsEnable, initShowState, layerIndex, preserveTextLevel', 'UND' => 1),
'code' => array('CPX' => 0, 'VAL' => '(200|500|601|602|603|610|620)'),
'color' => array('CPX' => 0, 'VAL' => '([0-9a-fA-F]){8}'), // hex values only
'colorMode' => array('CPX' => 0, 'VAL' => '(normal|random)'),
'cookie' => array('CPX' => 0, 'VAL' => '.*'),
'ColorStyle' => array('CPX' => 1, 'OPT'=>'antialias,color,colorMode', 'NOTES' => '', 'UND' => 1),
'cookie' => array('CPX' => 0, 'VAL' => '.*'),
'coordinates' => array('CPX' => 0, 'VAL' => '.*'), //'(\s*((?:\-*\d+\.*\d*)\s*,\s*){2}(?:\-*\d+\.*\d*)){1,}'), // ** TODO Is this correct!?
'Create' => array('CPX' => 1, 'VAL' => '.*'),
'Delete' => array('CPX' => 1, 'VAL' => '.*'),
'description' => array('CPX' => 0, 'VAL' => '.*'),
'Document' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'Document,Folder,GroundOverlay,LookAt,NetworkLink,Placemark,Region,Schema,ScreenOverlay,Snippet,Style,StyleMap,styleUrl,description,name,visibility'),
'drawOrder' => array('CPX' => 0, 'VAL' => '\d{1,2}'), // 1 to 99
'east' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'end' => array('CPX' => 0, 'OPT' => 'TimeInstant'),
'expires' => array('CPX' => 0, 'VAL' => '.*'),
'extrude' => array('CPX' => 0, 'VAL' => '(0|1)'),
'fill' => array('CPX' => 0, 'MAX_OCCURS' => 1,'VAL' => '(0|1)'),
'flyToView' => array('CPX' => 0, 'VAL' => '(0|1)'),
'Folder' => array('CPX' => 1, 'OPT' => 'Folder,GroundOverlay,LookAt,NetworkLink,Placemark,ScreenOverlay,Snippet,Style,styleUrl,description,name,open,visibility', 'MULTI_OCCUR' => 1),
'geomColor' => array('CPX' => 0, 'ALI' => 1, 'VAL' => 'color'),
'GeometryCollection' => array('CPX' => 1, 'ALI' => 1, 'VAL' => 'MultiGeometry'),
'geomScale' => array('CPX' => 0, 'ALI' => 1, 'VAL' => 'scale'),
'GroundOverlay' => array('CPX' => 1, 'OPT' => 'LookAt,color,drawOrder,name,rotation,visibility', 'REQ' => 'Icon,LatLonBox', 'MULTI_OCCUR' => 1),
'h' => array('CPX' => 0, 'VAL' => '\-*\d+'),
'heading' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'href' => array('CPX' => 0, 'VAL' => '.*'),
'Icon' => array('CPX' => 1, 'OPT' => 'h,refreshMode,refreshInterval,viewFormat,viewRefreshMode,viewBoundScale,w,x,y', 'REQ' => 'href', 'NOTES' => 'has undocumented attributes: palletteUrl'),
'IconStyle' => array('CPX' => 1, 'OPT' => 'Icon,color,colorMode,heading,scale','NOTE'=>'has child or attribute heading!?'),
'id' => array('ATTR' => 1, 'VAL' => '\w(\w|\d\_|)*'),
'ImageLink' => array('CPX' => 1, 'OPT' => 'h,w,x,y', 'UND' => 1),
'innerBoundaryIs' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'REQ' => 'LinearRing'),
'ItemIcon' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'REQ' => 'state,href'),
'key' => array('CPX' => 0, 'VAL' => '(normal|highlight)'),
'kml' => array('CPX' => 1, 'XMLNS'=>1, 'CHO' => 'Document,Folder,GroundOverlay,LookAt,NetworkLink,Placemark,ScreenOverlay', 'OPT' => 'NetworkLinkControl', 'NOTES' => 'undocumented attributes (orphans): center, outlineFill, inherit'),
'labelColor' => array('CPX' => 0, 'ALI' => 1, 'VAL' => 'color'),
'LabelStyle' => array('CPX' => 1, 'OPT' => 'color,colorMode,scale'),
'latitude' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'LatLonAltBox' => array('CPX' => 1, 'OPT' => 'alitutudeMode,maxAltitude,minAltitude', 'REQ' => 'north,east,south,west'),
'LatLonBox' => array('CPX' => 1, 'OPT' => 'rotation', 'REQ' => 'north,east,south,west'),
'LatLonXform' => array('CPX' => 1, 'OPT' => 'rotation', 'UND' => 1),
'LinearRing' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'REQ' => 'coordinates'),
'LineString' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'altitudeMode,extrude,tessellate', 'REQ' => 'coordinates'),
'LineStyle' => array('CPX' => 1, 'OPT' => 'color,colorMode,width'),
'linkName' => array('CPX' => 0, 'VAL' => '.*'),
'linkDescription' => array('CPX' => 0, 'VAL' => '.*'),
'linkSnippet' => array('CPX' => 0, 'VAL' => '.*'),
'Link' => array('CPX' => 1, 'REQ' => 'href', 'OPT' => 'refreshInterval,refreshMode,refreshPeriod,viewFormat,viewRefreshMode,viewRefreshTime,viewBoundScale'),
'linkName' => array('CPX' => 0, 'VAL' => '.*'),
'listItemType' => array('CPX' => 0, 'VAL' => '(checkHideChildren|radioFolder|checkOffOnly)'),
'ListStyle' => array('CPX' => 1, 'OPT' => 'bgColor,color,colorMode,ItemIcon,listItemType'),
'Location' => array('CPX' => 1, 'OPT' => 'altitude,latitude,longitude'),
'Lod' => array('CPX' => 1, 'OPT' => 'maxLodPixels,maxFadeExtent,minLodPixels,minFadeExtent'),
'longitude' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'LookAt' => array('CPX' => 1, 'OPT' => 'altitude,heading,range,tilt', 'REQ' => 'latitude,longitude'),
'maxAltitude' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'maxFadeExtent' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'maxLodPixels' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'message' => array('CPX' => 0, 'VAL' => '.*'),
'minAltitude' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'minFadeExtent' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'minLodPixels' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'minRefreshPeriod' => array('CPX' => 0, 'VAL' => '\d+'),
'Model' => array('CPX' => 1, 'OPT' => 'altitudeMode,extrude,Link,Location,Orientation,Scale', 'MULTI_OCCUR' => 1),
'MultiGeometry' => array('CPX' => 1, 'OPT' => 'altitudeMode,extrude,tessellate,LinearRing,LineString,Model,MultiGeometry,MultiLineString,MultiPoint,MultiPolygon,Point,Polygon'),
'MultiLineString' => array('CPX' => 1, 'OPT' => 'extrude', 'UND' => 1, 'REQ' => 'LineString'),
'MultiPoint' => array('CPX' => 1, 'OPT' => 'extrude', 'UND' => 1, 'REQ' => 'Point'),
'MultiPolygon' => array('CPX' => 1, 'OPT' => 'extrude', 'UND' => 1, 'REQ' => 'Polygon'),
'name' => array('CPX' => 0, 'VAL' => '.*'),
'NetworkLink' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'Url,Link,flyToView,name,refreshVisibility,Snippet,visibility'),
'NetworkLinkControl' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'cookie,expires,linkName,linkDescription,linkSnippet,message,minRefreshPeriod,Update'),
'north' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'ObjArrayField' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'name,type'),
'ObjField' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'name,type'),
'open' => array('CPX' => 0, 'VAL' => '(0|1)'),
'Orientation' => array('CPX' => 1, 'OPT' => 'tilt,roll,heading'),
'outerBoundaryIs' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'REQ' => 'LinearRing'),
'outline' => array('CPX' => 0, 'VAL' => '(0|1)'),
'overlayXY' => array('CPX' => 0, 'REQ_ATTRIB' => 'x,y,xunits,yunits'),
'parent' => array('CPX' => 0, 'MAX_OCCURS' => 1),
'Pair' => array('CPX' => 1, 'REQ' => 'key,styleUrl', 'MULTI_OCCUR' => 1),
'Placemark' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'GeometryCollection,LineString,LookAt,Model,MultiGeometry,MultiLineString,MultiPoint,MultiPolygon,Point,Polygon,Style,TimePeriod,address,description,name,Snippet,styleUrl,visibility', 'NOTES' => 'has attributes: showAddress, styleMode'),
'Point' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'altitudeMode,extrude', 'REQ' => 'coordinates'),
'Polygon' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'innerBoundaryIs,LinearRing,outerBoundaryIs,altitudeMode,extrude'),
'PolyStyle' => array('CPX' => 1, 'OPT' => 'color,colorMode,fill,outline'),
'range' => array('CPX' => 0, 'VAL' => '\d+\.*\d*'),
'refreshInterval' => array('CPX' => 0, 'VAL' => '\d+'),
'refreshMode' => array('CPX' => 0, 'VAL' => '(once|onInterval|onChange|onExpire)'),
'refreshPeriod' => array('CPX' => 0, 'UND' => 1, 'VAL' => '\d+', 'NOTES' => 'deprecated (by refreshInterval?)'),
'refreshVisibility' => array('CPX' => 0, 'VAL' => '(0|1)'),
'Region' => array('CPX' => 1, 'OPT' => 'Lod', 'REQ' => 'LatLonAltbox'),
'request' => array('CPX' => 0, 'VAL' => '(geocode)'),
'Response' => array('CPX' => 1, 'OPT' => 'Placemark,Status'),
'roll' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'rotation' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'scale' => array('CPX' => 0, 'VAL' => '\d+\.*\d*'),
'Scale' => array('CPX' => 1, 'OPT' => 'x,y,z'),
'Schema' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'ObjArrayField,ObjField,SimpleArrayField,SimpleField,name,parent'),
'ScreenOverlay' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'drawOrder,name,overlayXY,rotation,screenXY,size,visibility', 'REQ' => 'Icon'),
'screenXY' => array('CPX' => 0, 'REQ_ATTRIB' => 'x,y,xunits,yunits'),
'Search' => array('CPX' => 1, 'NOTES' => 'has attributes: searchString, serverUrl', 'UND' => 1),
'SimpleArrayField' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'name,type'),
'SimpleField' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'name,type'),
'size' => array('CPX' => 0, 'REQ_ATTRIB' => 'x,y,xunits,yunits'),
'Snippet' => array('CPX' => 0, 'OPT'=>'text', 'MXL' => 1),
'south' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'state' => array('CPX' => 1, 'VAL' => '(on|off|open|closed|featching0|fetching1|fetching2)'),
'Status' => array('CPX' => 1, 'OPT' => 'code,request'),
'Style' => array('CPX' => 1, 'OPT' => 'BalloonStyle,ColorStyle,Icon,IconStyle,LabelStyle,LineStyle,ListStyle,PolyStyle,StyleBlinker', 'MULTI_OCCUR' => 1), //, 'REQ_ATTRIB' => 'id'
'StyleBlinker' => array('CPX' => 1, 'OPT' => 'State', 'UND' => 1),
'StyleMap' => array('CPX' => 1, 'REQ' => 'Pair', 'REQ_ATTRIB' => 'id', 'MULTI_OCCUR' => 1),
'styleUrl' => array('CPX' => 0, 'MAX_OCCURS' => 1),
'targetHref' => array('CPX' => 0, 'VAL' => '.*'),
'tessellate' => array('CPX' => 0, 'VAL' => '(0|1)'),
'text' => array('CPX' => 0, 'VAL' => '.*', 'NOTES' => 'has replaceable variables: $[name], $[description]'),
'textColor' => array('CPX' => 0, 'VAL' => '([0-9a-fA-F]){8}'), // hex values only
'Theme' => array('CPX' => 1, 'OPT' => 'ThemePalette', 'NOTES' => 'has attributes/children: heightMapping, IconMapping, meters, name, pallette, randomColors, clampMode, colorMapping, description', 'UND' => 1),
'ThemePalette' => array('CPX' => 1, 'UND' => 1),
'tilt' => array('CPX' => 0, 'VAL' => '\d+\.*\d*'),
'Time' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'UND' => 1, 'NOTES'=>'has child/attributes: WorldRange, begin, end, enabled, now'),
'TimeInstant' => array('CPX' => 1, 'REQ' => 'timePosition', 'UND' => 1),
'TimePeriod' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'begin,end', 'UND' => 1),
'timePosition' => array('CPX' => 0, 'VAL' => '(\d{4})*(\-\d{2})*(\-\d{2})*(T\d{2}:\d{2}:\d{2})*', 'UND' => 1), // ** TODO This needs checking and testing
'type' => array('CPX' => 0, 'VAL' => '(uint|short|ushort|float|double|bool|string|wstring|sharedstring|sharedwstring|Vec2f|Vec3d|icon)'),
'Update' => array('CPX' => 1, 'VAL' => '.*'),
'Url' => array('CPX' => 1, 'OPT' => 'refreshInterval,refreshMode,viewFormat,viewRefreshMode,viewRefreshTime', 'REQ' => 'href'),
'View' => array('CPX' => 1, 'ALI' => 1, 'VAL' => 'LookAt'),
'viewBoundScale' => array('CPX' => 0),
'viewFormat' => array('CPX' => 0, 'VAL' => '.*'),
'viewRefreshMode' => array('CPX' => 0, 'VAL' => '(never|onRequest|onStop|onRegion)'),
'viewRefreshTime' => array('CPX' => 0, 'VAL' => '\d+'),
'visibility' => array('CPX' => 0, 'VAL' => '(0|1)'),
'w' => array('CPX' => 0, 'VAL' => '\d+'),
'west' => array('CPX' => 0, 'VAL' => '\-*\d+\.*\d*'),
'width' => array('CPX' => 0, 'VAL' => '\d+\.*\d*'),
'x' => array('CPX' => 0, 'ATTR' => 1, 'VAL' => '\d+'),
'y' => array('CPX' => 0, 'ATTR' => 1, 'VAL' => '\d+'),
'xunits' => array('ATTR' => 1, 'VAL' => '(pixels|insetPixels|fraction)'),
'yunits' => array('ATTR' => 1, 'VAL' => '(pixels|insetPixels|fraction)'),
'maxLines' => array('ATTR' => 1, 'VAL' => '\d+', 'UND' => 1)
);
$htmlspecial_kml = FALSE;
class KmlClass
{
protected $pl_comment;
protected $obj_tag_type;
protected $obj_tag_attribs = array();
protected $valid = TRUE;
protected $err_msg = array();
protected $tag_validation = TRUE; //turn on|off tag validation for entire class
protected $value_validation = TRUE; //turn on|off value validation for entire class
function __construct($pl_comment = "", $complex_tag_name, $child_tags = array(), $complex_tag_attrib = array())
{
global $htmlspecial_kml;
$this->obj_tag_type = $complex_tag_name;
$this->pl_comment = $pl_comment;
$is_complex_tag = TRUE;
$this->appendChild($child_tags, $complex_tag_attrib, $is_complex_tag);
}
/* Debugging and error recording functions */
function debugClass($msg, $function = "")
{
$this->valid = FALSE;
$this->err_msg[$function] = "Error: $msg
\n";
}
function getErrMsg($source)
{
echo "$source() says:\n";
echo "Mistake from: $this->pl_comment: $this->obj_tag_type \n";
echo '
';
print_r($this->err_msg);
echo "
\n
\n";
}
/* Functions for adding to the KML schema */
/*
function registerCustomSimpleSchemaTags()
Adds SIMPLE tags for a custom KML to the Schema Validation array above: $Relatiopnships.
Ex. This allows sensor types to be encoded in the KML for use with and
$[tag_name] or just for embedded xml storage of the data for rss, XSLT, other purposes.
Expects an associative array with the Complex tag parent as the key, and the value as another array of the simple children
$simple_tag_array = array(
'Placemark' => array('Temperature', 'Humidity', 'pl_lat', 'pl_lon'),
'GroundOverlay' => array('Projection', 'DataSource'),
);
This would add entries to the Placemark entry in $Relationships as follows:
'Placemark' => array('CPX' => 1, 'MULTI_OCCUR' => 1, 'OPT' => 'LineString,LookAt,Model,... Temperature, Humidity, pl_lat,pl_lon'...'
*/
function registerCustomSimpleSchemaTags($simple_tag_array = array() )
{
global $Relationships;
foreach ($simple_tag_array as $parent => $child_array)
{
$created_simple_tags = '';
//Create the schema/array entry for the child tag
foreach($child_array as $child)
{
if (isSet($Relationships[$child])) //Don't overwrite real KML tags by accident!
{
$this->debugClass(" <$child> is already a KML tag in the Schema!", "registerCustomSimpleSchemaTags()");
}
else
{
$Relationships[$child] = array('CPX' => 0, 'VAL' => '.*'); //Set new child as simple tag that can accept any value (preg: .*)
$created_simple_tags .= "$child,"; //add this tag to the string which will be added to the specified parent tag
}
}
//Add the new child tags to the parent's list of optional children
$Relationships[$parent]['OPT'] .= "," . rtrim($created_simple_tags, ',');
}
}
/*
Adds COMPLEX tags for a custom KML to the Schema Validation array above: $Relatiopnships).
Ex. 'DMS_Placemark' that will take the place of 'Placemark' to incorporate custom simple tags.
You must specify a Complex tag that will serve as the model for this custom Complex Tag.
Since the custom simple tags were added in to 'Placemark' in registerCustomSimpleSchemaTags(), we need
to copy over the entire attributes set to 'DMS_Placemark'. Then we must search the entire schema
array and find all the places 'Placemark' was a valid child (ie. folder, document, etc) and add
'DMS_Placemark' to that list.
Expects an associative array with the name of the tag as the array key whose attributes will
be copied to the new complex tag type, who's name is the value
$example_array = array(
'Placemark' => 'DMS_Placemark',
'GroundOverlay' => 'DMS_GroundOverlay'),
)
*/
function registerCustomComplexSchemaTags($complex_tag_array = array() )
{
global $Relationships;
foreach ($complex_tag_array as $original => $new_complex_tag)
{
if (isSet($Relationships[$new_complex_tag])) //Don't overwrite real KML tags by accident!
{
$this->debugClass(" <$new_complex_tag> is already a KML tag in the Schema!", "registerCustomComplexSchemaTags()");
return;
}
//Copy attributes from the "original"
$Relationships[$new_complex_tag] = $Relationships[$original];
//This searches the entire $Relationships kml schema array, for references to the tag
//and adds the new complex tag based on the parent
$where_to_check = array('REQ', 'OPT');
//Search entire $Relationships array
foreach ($Relationships as $search_tag_name => $attribs)
{
//for each searched tag, check the attributes in $where_to_check
foreach ($where_to_check as $attrib)
{
//Ignore this tag if it doesn't have one of the attributes we are interested in
if (isSet($Relationships[$search_tag_name][$attrib]))
{
//ie. 'refreshInterval,refreshMode,viewFormat,viewRefreshMode,viewRefreshTime'
$search_tags_children = &$Relationships[$search_tag_name][$attrib];
if (strpos($search_tags_children, $original) !== FALSE) //does this list have the original tag?
{
//if so, add the new complex tag to the end of the list
$Relationships[$search_tag_name][$attrib] = $Relationships[$search_tag_name][$attrib] . ",$new_complex_tag";
}
}
}// end foreach ($where_to_check as $attrib)
}// end foreach ($Relationships as $search_tag_name => $attribs)
}// end foreach ($complex_tag_array as $original => $new_complex_tag)
return;
}// end function
/* KML object assembly functions */
function appendChild($child_tags = array(), $complex_tag_attrib = array(), $check_for_req_child_tags = FALSE)
{
/*
Check if tags and attributes are valid
An error in the above tag validation function will not halt the KML assembly process.
Rather it will create a critical error, but still display the faulty KML to make it easier
to see the error
*/
if ($this->tag_validation)
{
$this->validateArgs($child_tags, $complex_tag_attrib, $check_for_req_child_tags);
}
$this->loadArgs($child_tags, $complex_tag_attrib);
if (!$this->valid)
{
$this->getErrMsg('appendChild');
$htmlspecial_kml = TRUE;
}
}
function validateArgs($child_tags, $complex_tag_attribs, $check_for_req_child_tags)
{
global $Relationships;
//Is this a valid KML tag?
if (array_key_exists($this->obj_tag_type, $Relationships))
{
//Get list of required and optional tags & attributes for this object (KMl type $this->obj_tag_type)
$req_tags = $this->getArrayFromSchema('REQ');
$opt_tags = $this->getArrayFromSchema('OPT');
$req_attrib = $this->getArrayFromSchema('REQ_ATTRIB');
//If called from appendChild(), skip req children check, because complex kml tags are built from
//the ground up, and all the required children have already been added to them
//When the complex tag is added as a new object, as from the constructor, we'll check to see if all
//the required children are there.
if ($check_for_req_child_tags)
{
//Are all the required children included? Skipped when creating simple tags
$this->checkReqChildren($req_tags, $child_tags);
}
//Are the provided children all valid kml tags? (ie. mispellings, etc)
$this->checkIfValidKmlTags($child_tags, $opt_tags, $req_tags);
//Are all the required tag attributes included?
$this->checkReqAttrib($req_attrib, $complex_tag_attribs);
//Are all the provided tag attributes valid KML?
$this->checkIfValidKmlAttrib($complex_tag_attribs);
}
else //This object tag type is not a valid KML tag
{
$this->debugClass(" <$this->obj_tag_type> isn't a a valid kml tag", "validateArgs(5)");
}
return $this->valid;
}
function getArrayFromSchema($schema_part)
{
global $Relationships;
return isset($Relationships[$this->obj_tag_type][$schema_part]) ?
explode(',', $Relationships[$this->obj_tag_type][$schema_part]) : array();
}
function checkReqChildren(&$req_tags, &$child_tags)
{
//Does this obj tag have all the required Children according to KML Schema?
foreach ($req_tags as $key => $req_kml_tag)
{
if (!array_key_exists($req_kml_tag, $child_tags) )
{
$this->debugClass(" <" . $this->obj_tag_type
. "> requires tag <$req_kml_tag>", "validateArgs() >> checkReqChildren()");
}
}
}
//Are all the Children tags of this obj tag valid kml tags?
function checkIfValidKmlTags(&$child_tags, &$opt_tags, &$req_tags)
{
foreach ($child_tags as $tag => $value)
{
if (!in_Array($tag, array_merge($opt_tags, $req_tags)))
{
$this->debugClass(" <$tag> isn't a child of tag: <"
. $this->obj_tag_type . ">", "validateArgs() >> checkIfValidKmlTags()");
}
}
}
//Were all the required attributes provided?
function checkReqAttrib(&$req_attrib, &$complex_tag_attribs)
{
foreach($req_attrib as $key => $attrib)
{
if (!array_key_exists($attrib, $complex_tag_attribs))
{
$this->debugClass(" You are missing the required '$attrib' attribute for <"
. $this->obj_tag_type . ">", "validateArgs() >> checkReqAttrib()");
}
}
}
//Are all the passed attributes valid kml attributes?
function checkIfValidKmlAttrib(&$complex_tag_attribs)
{
global $Relationships;
foreach($complex_tag_attribs as $key => $attrib)
{
if (!isset($Relationships[$key]['ATTR']))
{
$this->debugClass(" '$key' is not a valid kml attribute", "validateArgs() >> checkIfValidKmlAttrib()");
}
}
}
function loadArgs($child_tags, $complex_tag_attribs)
{
global $Relationships;
//Load any child tags
foreach ($child_tags as $tag => $value)
{
//If this tag can occur more than once, make an array: $this->$tag[]
if ((isset($Relationships[$tag]['MULTI_OCCUR']) && ($Relationships[$tag]['MULTI_OCCUR'] == 1)))
{
//Unlike tags that can only appear once, syntax for starting a new variable, or adding to it is the same
//If we are passing an array of objects/tags, iterate through and insert them all
if (is_array( $value ))
{
foreach($value as $i => $value2)
{
$this->{$tag}[] = $value2;
}
}
//Just a single object or tag to add, ie. the first of potentially more to follow
else
{
$this->{$tag}[] = $value;
}
}
//This tag can only occur once, so make it a regular variable $this->$tag
else
{
//This tag can't occur more than once!
if (isset( $this->{$tag} ))
{
$this->debugClass(" <" . $this->$tag . "> already exists in this <" . $this->obj_tag_type .
"> Schema says it doesn't supoprt MULTI_OCCURS!", "loadArgs()");
}
//It doesn't already exists, so try to create it!
else
{
//This tag might not already exist, but you are trying to pass in multiple tags as an array!
if (is_array( $value ))
{
$this->debugClass(" <" . $tag . "> already exists in this <" . $this->obj_tag_type .
"> Schema says it doesn't supoprt MULTI_OCCURS!", "loadArgs()");
}
//Everything looks OK, let's create that tag!
else
{
$this->$tag = ($this->value_validation) ? $this->validateValue($tag, $value) : $value;
}
}
}
}
//Load any tag attributes
foreach($complex_tag_attribs as $key => $attrib)
{
$this->obj_tag_attribs[$key] = $attrib;
}
}
function XXloadArgs($child_tags, $complex_tag_attribs)
{
global $Relationships;
//Load any child tags
foreach ($child_tags as $tag => $value)
{
//does member variable already exist?
if (isset( $this->{$tag} ))
{
//If this tag supports multiple occurances, add it to the array
if ((isset($Relationships[$tag]['MULTI_OCCUR']) && ($Relationships[$tag]['MULTI_OCCUR'] == 1)))
{
//If we are passing an array of objects/tags, iterate through and insert them all
if (is_array( $this->{$tag} ))
{
foreach($value as $i => $value2)
{
$this->{$tag}[] = $value2;
}
}
//Just a single object or tag to add, ie. the first of potentially more to follow
else
{
$this->{$tag}[] = $value;
}
}
//Must be a duplicate of a single occurance tag
else
{
$this->debugClass(" <" . $this->$tag . "> already exists in this <" . $this->obj_tag_type .
"> Schema says it doesn't supoprt MULTI_OCCURS!", "loadArgs()");
}
}
//Simple tag, just create it
else
{
//$this->$tag = $value;
if ($this->value_validation)
{
$this->$tag = $this->validateValue($tag, $value);
}
else
{
$this->$tag = $value;
}
}
}
//Load any tag attributes
foreach($complex_tag_attribs as $key => $attrib)
{
$this->obj_tag_attribs[$key] = $attrib;
}
}
/* Tag Value Validation Functions */
function validateValue($tag, $value)
{
global $Relationships;
if (isset($Relationships[$tag]['VAL'])) //if schema array has valid values for this tag...
{
$preg_or_array = $Relationships[$tag]['VAL'];
if (strpos($preg_or_array, '|') !== FALSE) //If the value string contains a | it is a list of valid values
{
return $this->valueArrayMatch($preg_or_array, $tag, $value);
}
else //otherwise this a a preg match pattern to check for validity
{
return $this->valuePregMatch($preg_or_array, $tag, $value);
}
}
else //No criteria for checking value validity
{
return $value;
}
}
// For this particular tag, is this value on the array of valid values?
function valueArrayMatch(&$array_valid_values, &$tag, &$value)
{
global $Relationships;
$strip_parens = trim($array_valid_values, '()');
//This is an array of possible values
$valid_value_array = explode('|', $strip_parens);
if (in_array($value, $valid_value_array) == TRUE)
{
return $value;
}
else
{
$this->debugClass(" '$value' is not a valid value for <" . $tag . "> ($strip_parens)", "valueArrayMatch()");
return "BAD VALUE";
}
}
function valuePregMatch(&$preg_pattern, &$tag, &$value)
{
global $Relationships;
$matches = "";
$preg_pattern = '/'. $preg_pattern .'/';
//not matches found, $value isn't valid.
//preg_match_all('/'. $preg_pattern .'/',$points,$matches)
if (preg_match($preg_pattern, $value) == 0)
{
$this->debugClass(" '$value' is not a valid value for <" . $tag . "> ($preg_pattern)", "valuePregMatch()");
return "BAD VALUE";
}
//Found a match, $value is valid
else
{
return $value;
}
}
/* KML object retrieval functions for building KML */
function getTagName()
{
return $this->obj_tag_type;
}
function getTabAttribs()
{
return $this->obj_tag_attribs;
}
}; //end class
//Iterates throught he entire object and returns it as XML
function generate_kml(&$pl_kml, &$obj, &$tab_chars = " ", $t = 1)
{
//tag name
$class_name = $obj->getTagName();
//assemble tag attributes
$complex_tag_attribs = $obj->getTabAttribs();
$attrib_string = "";
foreach($complex_tag_attribs as $key => $attribs)
{
$attrib_string .= " $key=\"$attribs\"";
}
//start creating kml
$pl_kml .= str_repeat($tab_chars, $t) . "<$class_name" . "$attrib_string>\n";
foreach ($obj as $var => $value)
{
//If this item is an object, recurse the function to get child tags
if (is_object($value))
{
generate_kml($pl_kml, $value, $tab_chars, $t+1);
}
//If this item is an array (of objects), iternate throught the array
// and then recurse the function to get child tags
elseif (is_array($value))
{
foreach ($value as $j => $obj_Array)
{
if (is_object($obj_Array))
{
generate_kml($pl_kml, $obj_Array, $tab_chars, $t+1);
}
else
{
echo $obj_Array;
echo "Error: Array doesn't contain objects!!: $var";
}
}
}
//If this is just a simple tag...
else
{
$pl_kml .= str_repeat($tab_chars, $t + 1) . "<$var>" . $value . "$var>\n";
}
}
$pl_kml .= str_repeat($tab_chars, $t) . "$class_name>\n";
}
?>