logVotable = fopen(LOG_DIR . "VOTable.log", "a"); } function addLog($msg) { if (DEBUG_MODE) fwrite($this->logVotable, date("h:i:s A") . ": " . $msg . "\n"); } function load($fileName) { $this->addLog("File name" . $fileName); $this->is_little_endian = array_values(unpack('L1L', pack('V', 1)))[0] == 1; // see http://php.net/manual/en/domdocument.load.php#91384 $opts = array( 'http' => array('user_agent' => 'PHP libxml agent') ); $context = stream_context_create($opts); libxml_set_streams_context($context); $this->xml = new DomDocument(); $res = $this->xml->load($fileName); if(!$res) { $this->addLog("Can not load VOTAble."); return false; } $this->checkIDAttribute(); /*if ($this->xml->namespaceURI == '') { $this->addLog("File don't have a namespace defined\n"); if (!$this->xml->createAttributeNS('http://www.ivoa.net/xml/VOTable/v1.1','xmlns')) $this->addLog("Cannot create namespace attribute\n"); } $this->addLog($this->xml->namespaceURI."\n");*/ $rootNamespace = $this->xml->lookupNamespaceUri($this->xml->namespaceURI); $this->xp = new domxpath($this->xml); $this->xp->registerNameSpace('x', $rootNamespace); $this->addLog($fileName . " loaded."); return true; } function isValidSchema() { if (!$this->xml) return FALSE; $infos = $this->xp->query($this->queryResourceInfo()); foreach($infos as $info) { if($info->getAttribute('value') == 'ERROR') { $this->addLog("There is an error on the VOTable: " . $info->textContent); return FALSE; } } //ToDo - BRE - add validation!! return TRUE; if (DEBUG_MODE) libxml_use_internal_errors(true); $vers = $this->getVersion(); $this->addLog("VOTable version : ".$vers."\n"); $result = FALSE; switch ($vers) { case '1.2' : $result = $this->xml->schemaValidate(XMLPATH.'VOTable-1.2.xsd'); case '1.0' : $result = $this->xml->schemaValidate(XMLPATH.'VOTable-1.0.xsd'); default : $result = $this->xml->schemaValidate(XMLPATH.'VOTable-1.1.xsd'); } if (DEBUG_MODE) { $errors = libxml_get_errors(); foreach ($errors as $error) { $msg = ''; switch ($error->level) { case LIBXML_ERR_WARNING: $msg .= ("WARNING ".$error->code.": "); break; case LIBXML_ERR_ERROR: $msg .= ("ERROR ".$error->code.": "); break; case LIBXML_ERR_FATAL: $msg .= ("FATAL ".$error->code.": "); break; } $msg .= ($error->message." - In line : ".$error->line." - Of file : ".$error->file."\n"); $this->addLog($msg); } libxml_use_internal_errors(false); } return $result; } protected function queryResource() { return "//x:RESOURCE"; } protected function queryResourceInfo() { return $this->queryResource()."/x:INFO"; } protected function queryTable() { return $this->queryResource()."/x:TABLE"; } protected function queryDescription() { return $this->queryTable()."/x:DESCRIPTION"; } protected function queryFields() { return $this->queryTable()."/x:FIELD"; } protected function queryField($field_id) { return $this->queryFields()."[@ID='".$field_id."']"; } protected function queryFieldByName($field_id) { return $this->queryFields()."[@name='".$field_id."']"; } protected function queryFieldDescription($field_id) { return $this->queryField($field_id)."/x:DESCRIPTION"; } protected function queryData() { return $this->queryTable()."/x:DATA"; } protected function queryTableData() { return $this->queryData()."/x:TABLEDATA"; } protected function queryTR() { return $this->queryTableData()."/x:TR"; } protected function queryBinaryData() { return $this->queryData()."/x:BINARY"; } protected function queryStream() { return $this->queryBinaryData()."/x:STREAM"; } public function getVersion() { if (!$this->xml) return ''; $root = $this->xml->documentElement; return $root->getAttribute('version'); } public function getDescription() { if (!$this->xp) return ''; $desc = $this->xp->query($this->queryDescription()); if ($desc->length < 1) return ''; return $desc->item(0)->nodeValue; } private function get_row_size($field_node) { $datatype = $field_node->getAttribute("datatype"); if($datatype == 'boolean') { return 1; } switch($datatype) { case 'unsignedByte': case 'char': $block_size = 1; break; case 'unicodeChar': case 'short': $block_size = 2; break; case 'int': case 'float': $block_size = 4; break; case 'long': case 'double': case 'float_complex': $block_size = 8; break; case 'double_complex': $block_size = 16; default: $block_size = 0; break; } if($field_node->getAttribute("arraysize") == NULL) { $array_size = $block_size; } else if("*" == $field_node->getAttribute("arraysize")) { $array_size = unpack("Ns", substr($this->stream, $this->c, 4))["s"] * $block_size; $this->c+=4; } else { $array_size = (int)($field_node->getAttribute("arraysize")) * $block_size; } return $array_size; } /** Get the VOTable stream content.*/ public function parseStream() { $data = Array(); $fields = $this->xp->query($this->queryFields()); $nb_columns = $fields->length; $row = Array(); $n_value = 0; // index of current value $this->c = 0; // initialize cursor position. $query_stream = $this->xp->query($this->queryStream())->item(0); if($query_stream == NULL) return NULL; $this->stream = base64_decode($query_stream->textContent); $stream_len = strlen($this->stream); if($stream_len == 0) return NULL; while($this->c < strlen($this->stream)) { $col_id = $n_value % $nb_columns; $field_node = $fields[$col_id]; // echo "c=$this->c, n_value=$n_value, nb_columns=$nb_columns, col_id=$col_id, column_name=" . $field_node->getAttribute("ID") . "\n"; if($col_id == 0) { $row = Array(); } $row[$field_node->getAttribute("ID")] = $this->process_datablock($field_node); if($col_id == $nb_columns-1) { array_push($data, $row); } $n_value+=1; // echo "Char pos: $this->c\n"; } return $data; } private function JDTodate($jd) { list($month, $day, $year) = split('/', JDToGregorian($jd)); return "$day/$month/$year"; } # because unpack for floats is hardware dependant // private function big_endian_unpack ($format, $data) { // $ar = unpack ($format, $data); // $vals = array_values ($ar); // echo "vals: " . json_encode($vals) . "\n"; // $f = explode ('/', $format); // $i = 0; // foreach ($f as $f_k => $f_v) { // $repeater = intval (substr ($f_v, 1)); // // if ($repeater == 0) { // $repeater = 1; // } // if ($f_v{1} == '*') { // $repeater = count ($ar) - $i; // } // if ($f_v{0} != 'd') { // $i += $repeater; continue; // } // $j = $i + $repeater; // // for ($a = $i; $a < $j; ++$a) { // $p = pack ('d', $vals[$i]); // $p = strrev ($p); // ++$i; // } // } // $a = 0; // foreach ($ar as $ar_k => $ar_v) { // $ar[$ar_k] = $vals[$a]; // ++$a; // } // return $ar; // } private function process_datablock($field_node) { $data_type = $field_node->getAttribute("datatype"); $row_size = $this->get_row_size($field_node); $substr = substr($this->stream, $this->c, $row_size); switch ($data_type) { case 'boolean': case 'unsignedByte': $b = $substr; $res = $b == "T" || $b == "t" || $b == "1"; break; case 'char': case 'unicodeChar': $res = $row_size!=0 ? $substr : NULL; break; case 'short': $res = unpack("ss", $substr)["s"]; break; case 'int': $res = unpack("Ns", $substr)["s"]; break; case 'long': $res = unpack("Js", $substr)["s"]; // /!\ J -> PHP 5.6 only break; case 'float': $res = unpack("fs", $substr)["s"]; // If machine is little endian: if($this->is_little_endian) { $res = unpack('f1f', strrev(pack('f', $res)))["f"]; } break; case 'double': $res = unpack("ds", $substr)["s"]; // If machine is little endian: if($this->is_little_endian) { $res = unpack('d1d', strrev(pack('d', $res)))["d"]; } break; default: $res = NULL; $this->addLog("Unknown character: $data_type"); break; } $this->c+=$row_size; // echo $field_node->getAttribute("ID") . "($data_type $row_size) = " . json_encode($res) . "\n"; return $res; } public function getFirstTR() { if (!$this->xp) return NULL; /*$trs = $this->xp->query($this->queryTR()); if ($trs->length < 1) return NULL; return $trs->item(0);*/ $tabledatas = $this->xp->query($this->queryTableData()); if ($tabledatas->length < 1) return NULL; $tabledata = $tabledatas->item(0); $node = $tabledata->firstChild; while($node && ($node->nodeType != 1) && ($node->nodeName != "TR")) $node = $node->nextSibling; return $node; } public function getNextTR($tr) { if (!$this->xp) return NULL; while($tr->nextSibling && ($tr->nextSibling->nodeType != 1) && ($node->nodeName != "TR")) $tr = $tr->nextSibling; return $tr->nextSibling; } public function getTDValueByFieldIndex($tr,$field_index) { if (!$this->xp) return NULL; $tds = $tr->getElementsByTagName("TD"); if (($tds->length < 1) || ($field_index >= $tds->length)) return NULL; return $tds->item($field_index)->nodeValue; } protected function isTimeField($field) { if (!$this->xp) return FALSE; return (($field->getAttribute("ucd") == "time.epoch") && ($field->getAttribute("xtype") == "dateTime")); } public function getTimeFieldIndex() { if (!$this->xp) return -1; $fields = $this->xp->query($this->queryFields()); if ($fields->length < 1) return -1; for ($i = 0; $i < $fields->length; $i++) if ($this->isTimeField($fields->item($i))) return $i; return -1; } protected function getFieldByID($field_id) { if (!$this->xp) return NULL; $fields = $this->xp->query($this->queryFields()); if ($fields->length < 1) return NULL; foreach ($fields as $field) if ($field->getAttribute("ID") == $field_id) return $field; return NULL; } protected function getFieldByName($field_id) { if (!$this->xp) return NULL; $fields = $this->xp->query($this->queryFieldByName($field_id)); if ($fields->length < 1) return NULL; foreach ($fields as $field) if ($field->getAttribute("name") == $field_id) return $field; return NULL; } protected function checkIDAttribute(){ $fields = $this->xml->getElementsByTagName('FIELD'); $i = 0; foreach ($fields as $field){ $i++; if (!$field->hasAttribute("ID")){ $field->setAttribute("ID", "col".$i); } } $this->xml->saveXML(); } public function getFieldIndexByID($field_id) { if (!$this->xp) return -1; $fields = $this->xp->query($this->queryFields()); if ($fields->length < 1) return -1; for ($i = 0; $i < $fields->length; $i++) if ($fields->item($i)->getAttribute("ID") == $field_id) return $i; return -1; } public function getStartStop() { if (!$this->xp) return '0 0'; $timeIndex = $this->getTimeFieldIndex(); if ($timeIndex < 0) return '0 0'; $tr = $this->getFirstTR(); if (!$tr) return '0 0'; $start = $this->getTDValueByFieldIndex($tr,$timeIndex); $stop = $start; while ($tr) { $stop = $this->getTDValueByFieldIndex($tr,$timeIndex); $tr = $this->getNextTR($tr); } if (!$start) $start = 0; else $start = strtotime($start); if (!$stop) $stop = 0; else $stop = strtotime($stop); return $start." ".$stop; } public function getFieldInfoByID($field_id) { if (!$this->xp) return array("id" => $field_id, "error" => "No file loaded"); $field = $this->getFieldByID($field_id); if (!$field) $field = $this->getFieldByName($field_id); if (!$field) return array("id" => $field_id, "error" => "This field doesn't exist"); return $this->getFieldInfo($field); } public function getFieldInfo($field) { if (!$this->xp) return array("id" => $field_id, "error" => "No file loaded"); $description = ''; $desc = $field->getElementsByTagName("DESCRIPTION"); if ($desc->length >= 1) $description = $desc->item(0)->nodeValue; $size = $field->getAttribute("arraysize"); if ($size == '') $size = 1; else $size = intval($size); switch ($field->getAttribute("datatype")) { case "short" : $type = "SHORT"; break; case "int" : $type = "INTEGER"; break; case "long" : case "double" : $type = "DOUBLE"; break; default : $type = "FLOAT"; } if (!$field->getAttribute("ID")) $id = "col".$n; else $id = $field->getAttribute("ID"); return array("id" => $field->getAttribute("ID"), "type" => $type, "name" => $field->getAttribute("name"), "ucd" => $field->getAttribute("ucd"), "unit" => $field->getAttribute("unit"), "size" => $size, "description" => $description ); } public function getFieldsInfo() { if (!$this->xp) return array("error" => "No file loaded"); $fields_info = array(); $fields = $this->xp->query($this->queryFields()); if ($fields->length < 1) return $fields_info; foreach ($fields as $field) { if ($this->isTimeField($field)) continue; array_push($fields_info,$this->getFieldInfo($field)); } return $fields_info; } public function getSamplings() { if (!$this->xp) return array("minSampling" => 0, "maxSampling" => 0); $timeIndex = $this->getTimeFieldIndex(); if ($timeIndex < 0) return array("minSampling" => 0, "maxSampling" => 0); $tr = $this->getFirstTR(); if (!$tr) return array("minSampling" => 0, "maxSampling" => 0); $prevTime = 0; while ($tr) { $time = $this->getTDValueByFieldIndex($tr,$timeIndex); if ($time) { $time = strtotime($time); if (($prevTime > 0) && ($time-$prevTime > 0)) $deltaT[$time-$prevTime]++; $prevTime = $time; } $tr = $this->getNextTR($tr); } $minSampling = +1.e31; $maxSampling = 0.0; foreach ($deltaT as $key => $value) { if ($value/count($deltaT) < 0.10) continue; if ($key < $minSampling) $minSampling = $key; if ($key > $maxSampling) $maxSampling = $key; } return array("minSampling" => $minSampling, "maxSampling" => $maxSampling); } public function args2vector($file, $paramID){ $argsArr = explode('_', $paramID); $dom = new DOMDocument(); $dom->load($file); $fields = $dom->getElementsByTagName('FIELD'); $table = $dom->getElementsByTagName('TABLE')->item(0); $i=0; foreach ($fields as $field){ if ($field->getAttribute('name') == $argsArr[0]){ $unit = $field->getAttribute('unit'); $ucd = $field->getAttribute('ucd'); $datatype = $field->getAttribute('datatype'); $firstTD = $i; } $i++; } if ($firstTD > 0){ $table->removeChild($fields->item($firstTD + 2)); $table->removeChild($fields->item($firstTD + 1)); $table->removeChild($fields->item($firstTD)); $i = 0; foreach ($fields as $field){ $i++; if (strpos($field->getAttribute('ID'),'col') !== FALSE){ $field->setAttribute('ID', 'col'.$i); $dom->saveXML(); } } $group = $dom->createElement('GROUP'); $group->appendChild(new DOMAttr('ID', 'info_'.$paramID)); $table->appendChild($group); $param = $dom->createElement('PARAM'); $param->appendChild(new DOMAttr('ID', 'components_'.$paramID)); $param->appendChild(new DOMAttr('name', 'components_'.$paramID)); $param->appendChild(new DOMAttr('datatype', 'char')); $param->appendChild(new DOMAttr('arraysize', '*')); $param->appendChild(new DOMAttr('value', $argsArr[0].' '.$argsArr[1].' '.$argsArr[2] )); $group->appendChild($param); $new_field = $dom->createElement('FIELD'); $new_field->appendChild(new DOMAttr('ID', $paramID)); $new_field->appendChild(new DOMAttr('name', $paramID)); $new_field->appendChild(new DOMAttr('datatype', $datatype)); $new_field->appendChild(new DOMAttr('arraysize', '3')); $new_field->appendChild(new DOMAttr('unit', $unit)); $new_field->appendChild(new DOMAttr('ucd', $ucd)); $new_field->appendChild(new DOMAttr('ref', 'info_'.$paramID)); $table->appendChild($new_field); $trs = $dom->getElementsByTagName('TR'); foreach($trs as $tr){ $tds = $tr->getElementsByTagName('TD'); $value = trim($tds->item($firstTD)->nodeValue).' '.trim($tds->item($firstTD + 1)->nodeValue).' '.trim($tds->item($firstTD + 2)->nodeValue); $toRemote = $tds->item($firstTD); $tr->removeChild($toRemote); $toRemote = $tds->item($firstTD); $tr->removeChild($toRemote); $toRemote = $tds->item($firstTD); $tr->removeChild($toRemote); $td = $dom->createElement('TD', $value); $tr->appendChild($td); } $dom->save($file); } } } ?>