| 136 | | |
|---|
| 137 | | use constant USE_DEBUG_PRINTS => 1; |
|---|
| 138 | | |
|---|
| 139 | | sub samos_moving_point |
|---|
| 140 | | { |
|---|
| 141 | | # top timestamp |
|---|
| 142 | | my( $ncFilename, $strObsKMLFilePath,$iLastNTimeStamps ) = @_; |
|---|
| 143 | | |
|---|
| 144 | | # Turn off fatal error aborts. We need to allow some attributes to error out w/o crashing. |
|---|
| 145 | | NetCDF::opts(VERBOSE) ; |
|---|
| 146 | | |
|---|
| 147 | | # |
|---|
| 148 | | # Open the netCDF file |
|---|
| 149 | | |
|---|
| 150 | | my $ncid = NetCDF::open($ncFilename, 0); |
|---|
| 151 | | if($ncid < 0) |
|---|
| 152 | | { |
|---|
| 153 | | die "ABORT! Cannot open netCDF file.\n"; |
|---|
| 154 | | } |
|---|
| 155 | | |
|---|
| 156 | | # |
|---|
| 157 | | # General file query |
|---|
| 158 | | my( $ndims,$nvars,$natts,$recdim ); |
|---|
| 159 | | my $inquire = NetCDF::inquire($ncid,$ndims,$nvars,$natts,$recdim); |
|---|
| 160 | | if( $inquire < 0 ) |
|---|
| 161 | | { |
|---|
| 162 | | die "ABORT! Cannot query netCDF file.\n"; |
|---|
| 163 | | } |
|---|
| 164 | | |
|---|
| 165 | | |
|---|
| 166 | | my $bWriteobsKMLFile = 0; |
|---|
| 167 | | if( length($strObsKMLFilePath) > 0 ) |
|---|
| 168 | | { |
|---|
| 169 | | $bWriteobsKMLFile = 1; |
|---|
| 170 | | } |
|---|
| 171 | | |
|---|
| 172 | | #The XML file that has our obsKML units conversion. WE use it here to convert the units string names from the netcdf |
|---|
| 173 | | #file into the strings we use in the obsKML and then in our database. |
|---|
| 174 | | my $strUnitsXMLFilename; |
|---|
| 175 | | $strUnitsXMLFilename = './UnitsConversion.xml'; |
|---|
| 176 | | my $XMLControlFile = XML::LibXML->new->parse_file("$strUnitsXMLFilename"); |
|---|
| 177 | | |
|---|
| 178 | | #Create the platform handle |
|---|
| 179 | | my $id='None'; |
|---|
| 180 | | my $attget = NetCDF::attget($ncid,NetCDF::GLOBAL,'ID',\$id); |
|---|
| 181 | | if( $attget < 0 ) |
|---|
| 182 | | { |
|---|
| 183 | | print( "No ID provided in file.\n" ); |
|---|
| 184 | | } |
|---|
| 185 | | #my $strPlatformID = "samos.$id.ship"; |
|---|
| 186 | | my $strPlatformID = $id; |
|---|
| 187 | | |
|---|
| 188 | | my $institution_url_value = 'http://samos.coaps.fsu.edu/html/data.php'; |
|---|
| 189 | | |
|---|
| 190 | | my @KMLTimeStamp; |
|---|
| 191 | | my ( $rTime, $rLong, $rLat ); |
|---|
| 192 | | my @vars; |
|---|
| 193 | | my @varIgnoreList = ( "platform heading", |
|---|
| 194 | | "platform course", |
|---|
| 195 | | "platform relative wind direction", |
|---|
| 196 | | "platform speed over ground", |
|---|
| 197 | | "platform relative wind speed", |
|---|
| 198 | | "calender date", |
|---|
| 199 | | "time of day", |
|---|
| 200 | | "file history information" |
|---|
| 201 | | ); |
|---|
| 202 | | |
|---|
| 203 | | my $this_var_name; |
|---|
| 204 | | my $timeCnt = 0; |
|---|
| 205 | | for (my $i = 0; $i < $nvars; $i++) |
|---|
| 206 | | { |
|---|
| 207 | | my $this_var_type; |
|---|
| 208 | | my $this_var_dims; |
|---|
| 209 | | my @this_var_dimid = (); |
|---|
| 210 | | my $this_var_natts; |
|---|
| 211 | | my $this_standard_name = ''; |
|---|
| 212 | | my $varinq = NetCDF::varinq($ncid, |
|---|
| 213 | | $i, |
|---|
| 214 | | \$this_var_name, |
|---|
| 215 | | \$this_var_type, |
|---|
| 216 | | \$this_var_dims, |
|---|
| 217 | | \@this_var_dimid, |
|---|
| 218 | | \$this_var_natts); |
|---|
| 219 | | if ($varinq < 0) |
|---|
| 220 | | { |
|---|
| 221 | | die "ABORT! Cannot get to variables.\n"; |
|---|
| 222 | | } |
|---|
| 223 | | my $attget = NetCDF::attget($ncid, $i, 'long_name', \$this_standard_name); |
|---|
| 224 | | if (substr($this_standard_name,length($this_standard_name)-1) eq chr(0)) |
|---|
| 225 | | { |
|---|
| 226 | | chop($this_standard_name); |
|---|
| 227 | | } |
|---|
| 228 | | if( isInIgnoreList( \@varIgnoreList, $this_standard_name ) == 0 ) |
|---|
| 229 | | { |
|---|
| 230 | | if ($attget >= 0) |
|---|
| 231 | | { |
|---|
| 232 | | if( $this_var_dims <= 0 ) |
|---|
| 233 | | { |
|---|
| 234 | | die( "ABORT! $this_standard_name has no dimensions.\n" ); |
|---|
| 235 | | } |
|---|
| 236 | | my $refArrayVarData; |
|---|
| 237 | | #print( "this_var_name: $this_var_name this_standard_name: $this_standard_name\n" ); |
|---|
| 238 | | |
|---|
| 239 | | my %VariableHash; |
|---|
| 240 | | netcdfFuncs::ncSetupVariableInfo( $this_standard_name, |
|---|
| 241 | | $this_var_name, |
|---|
| 242 | | \%VariableHash, |
|---|
| 243 | | $i, |
|---|
| 244 | | $this_var_dims, |
|---|
| 245 | | \@this_var_dimid, |
|---|
| 246 | | $this_var_natts, |
|---|
| 247 | | $ncid, |
|---|
| 248 | | $XMLControlFile ); |
|---|
| 249 | | netcdfFuncs::ncGetData( $this_standard_name, |
|---|
| 250 | | $this_var_name, |
|---|
| 251 | | \%VariableHash, |
|---|
| 252 | | $ncid ); |
|---|
| 253 | | #Save the slot for the time. |
|---|
| 254 | | if( $this_standard_name =~ /^time$/) |
|---|
| 255 | | { |
|---|
| 256 | | $rTime = \%VariableHash; |
|---|
| 257 | | |
|---|
| 258 | | $timeCnt = %$rTime->{'var_name'}{$this_var_name}{'dim_name'}{'time'}{'dim_size'}; |
|---|
| 259 | | my $strUnits = %$rTime->{'var_name'}{$this_var_name}{'units'}; |
|---|
| 260 | | my @Data = %$rTime->{'var_name'}{$this_var_name}{'data'}; |
|---|
| 261 | | my $iDataSize = @{$Data[0]}; |
|---|
| 262 | | if( $timeCnt != $iDataSize ) |
|---|
| 263 | | { |
|---|
| 264 | | die( "ABORT! Time Dimension: $timeCnt does not match the data count: $iDataSize!\n"); |
|---|
| 265 | | } |
|---|
| 266 | | } |
|---|
| 267 | | elsif ($this_standard_name =~ /^latitude$/) |
|---|
| 268 | | { |
|---|
| 269 | | #Save a reference to the longitude hash for easy access. |
|---|
| 270 | | $rLat = \%VariableHash; |
|---|
| 271 | | } |
|---|
| 272 | | elsif ($this_standard_name =~ /^longitude$/) |
|---|
| 273 | | { |
|---|
| 274 | | $rLong = \%VariableHash; |
|---|
| 275 | | } |
|---|
| 276 | | else |
|---|
| 277 | | { |
|---|
| 278 | | %VariableHash->{'var_name'}{$this_standard_name}{'process_function'} = \&ProcessSamosVar; |
|---|
| 279 | | push( @vars, {%VariableHash} ); |
|---|
| 280 | | } |
|---|
| 281 | | } |
|---|
| 282 | | } |
|---|
| 283 | | } |
|---|
| 284 | | my $iStartingNdx = 0; |
|---|
| 285 | | if( $iLastNTimeStamps > 0 ) |
|---|
| 286 | | { |
|---|
| 287 | | #If we have more entries in the time_values array, let's set the starting index at the spot in the array which |
|---|
| 288 | | #will get us to the first of the N time stamps. |
|---|
| 289 | | print( "iTimeCnt: $timeCnt iLastNTimeStamps: $iLastNTimeStamps\n" ); |
|---|
| 290 | | if( $timeCnt > $iLastNTimeStamps ) |
|---|
| 291 | | { |
|---|
| 292 | | $iStartingNdx = $timeCnt - $iLastNTimeStamps; |
|---|
| 293 | | if( USE_DEBUG_PRINTS ) |
|---|
| 294 | | { |
|---|
| 295 | | print( "moving_point()::Printing Last: $iLastNTimeStamps out of $timeCnt entries. Starting Index: $iStartingNdx. $ncid\n" ); |
|---|
| 296 | | } |
|---|
| 297 | | } |
|---|
| 298 | | elsif( $iLastNTimeStamps > $timeCnt ) |
|---|
| 299 | | { |
|---|
| 300 | | print( "moving_point()::iLastNTimeStamps: $iLastNTimeStamps is greater than total number of times in file. Resetting to iLastNTimeStamps to $timeCnt\n" ); |
|---|
| 301 | | $iLastNTimeStamps = $timeCnt; |
|---|
| 302 | | } |
|---|
| 303 | | } |
|---|
| 304 | | my %ObsHash; |
|---|
| 305 | | # write data to file(s) |
|---|
| 306 | | my $iVarCnt = @vars; |
|---|
| 307 | | if( $bWriteobsKMLFile ) |
|---|
| 308 | | { |
|---|
| 309 | | #TIme is always the outer loop. Each measurement is taken at a specific time. |
|---|
| 310 | | my $iTimeNdx = $iStartingNdx; |
|---|
| 311 | | for( ; $iTimeNdx < $timeCnt; $iTimeNdx++ ) |
|---|
| 312 | | { |
|---|
| 313 | | my $strTimeVal = getDateTimeFromEpoch( $rTime, $iTimeNdx ); |
|---|
| 314 | | my( $LatVal, $LonVal ); |
|---|
| 315 | | netcdfFuncs::ncGetLatLong( $rLat, $rLong, $iTimeNdx, \$LatVal, \$LonVal ); |
|---|
| 316 | | if( $LonVal > 180 ) |
|---|
| 317 | | { |
|---|
| 318 | | $LonVal = -1 * (360.0 - $LonVal); |
|---|
| 319 | | } |
|---|
| 320 | | #Fix precision |
|---|
| 321 | | $LatVal = sprintf( '%.5f', $LatVal ); |
|---|
| 322 | | $LonVal = sprintf( '%.5f', $LonVal ); |
|---|
| 323 | | obsKMLSubRoutines::KMLAddPlatformHashEntry( $strPlatformID, |
|---|
| 324 | | $institution_url_value, |
|---|
| 325 | | $LatVal, |
|---|
| 326 | | $LonVal, |
|---|
| 327 | | \%ObsHash, |
|---|
| 328 | | $strTimeVal ); |
|---|
| 329 | | |
|---|
| 330 | | for( my $iVar = 0; $iVar < $iVarCnt; $iVar++ ) |
|---|
| 331 | | { |
|---|
| 332 | | my $refVarData = $vars[$iVar]; |
|---|
| 333 | | foreach my $strVarName ( sort keys %{$refVarData->{'var_name'}} ) |
|---|
| 334 | | { |
|---|
| 335 | | #print( "Var: $strVarName\n" ); |
|---|
| 336 | | if( defined $refVarData->{'var_name'}{$strVarName}{'process_function'} ) |
|---|
| 337 | | { |
|---|
| 338 | | my $ProcFunc = $refVarData->{'var_name'}{$strVarName}{'process_function'}; |
|---|
| 339 | | &$ProcFunc( $strPlatformID, |
|---|
| 340 | | $strVarName, |
|---|
| 341 | | $refVarData, |
|---|
| 342 | | $strTimeVal, |
|---|
| 343 | | $iTimeNdx, |
|---|
| 344 | | $LatVal, |
|---|
| 345 | | $LonVal, |
|---|
| 346 | | \%ObsHash, |
|---|
| 347 | | \@vars ); |
|---|
| 348 | | } |
|---|
| 349 | | } |
|---|
| 350 | | } |
|---|
| 351 | | } |
|---|
| 352 | | my $iCnt = keys( %ObsHash ); |
|---|
| 353 | | #print( "Cnt: $iCnt\n" ); |
|---|
| 354 | | if( $iCnt ) |
|---|
| 355 | | { |
|---|
| 356 | | |
|---|
| 357 | | my $strXMLPath; |
|---|
| 358 | | my @aSrcFileParts = split( /\//, $ncFilename ); |
|---|
| 359 | | my $strSrcFileName = @aSrcFileParts[-1]; |
|---|
| 360 | | #Break the filename and extension apart. |
|---|
| 361 | | my $iPos = rindex( $strSrcFileName, '.' ); |
|---|
| 362 | | my $strFileName = $strSrcFileName; |
|---|
| 363 | | if( $iPos != -1 ) |
|---|
| 364 | | { |
|---|
| 365 | | $strFileName = substr( $strSrcFileName, 0, $iPos ); |
|---|
| 366 | | } |
|---|
| 367 | | #Use the source netcdf filename. |
|---|
| 368 | | #Add the $strDate which gives us a unique file name. Some providers don't uniquely name the netcdf files, so to prevent overwritting of my |
|---|
| 369 | | #kml, I add the time. |
|---|
| 370 | | $strXMLPath = $strObsKMLFilePath . $strFileName . '.kml'; |
|---|
| 371 | | print( "XMLFilePath: $strXMLPath\n" ); |
|---|
| 372 | | obsKMLSubRoutines::BuildKMLFile( \%ObsHash, $strXMLPath ); |
|---|
| 373 | | } |
|---|
| 374 | | } |
|---|
| 375 | | } |
|---|
| 376 | | |
|---|
| 377 | | ####################################################################################################################### |
|---|
| 378 | | # ProcessVariable |
|---|
| 379 | | ####################################################################################################################### |
|---|
| 380 | | sub ProcessSamosVar #() |
|---|
| 381 | | { |
|---|
| 382 | | my( $strPlatformID, $strVarName, $refVarData, $strTimeVal, $iTimeNdx, $LatVal, $LonVal, $rObsHash ) = @_; |
|---|
| 383 | | |
|---|
| 384 | | #We don't want to add the flags as a obs into the kml. |
|---|
| 385 | | if( $strVarName eq "quality control flags" ) |
|---|
| 386 | | { |
|---|
| 387 | | return; |
|---|
| 388 | | } |
|---|
| 389 | | #For fixed ADCP we have dimensions of time and z. Time is fixed per measurement, and the z count |
|---|
| 390 | | #tells us how man data points we need to read for a given time. |
|---|
| 391 | | my $iDataNdx = $iTimeNdx; |
|---|
| 392 | | my $DimCnt = 0; |
|---|
| 393 | | my $iDataCnt = 0; |
|---|
| 394 | | |
|---|
| 395 | | foreach my $strDimName ( sort keys %{$refVarData->{'var_name'}{$strVarName}{'dim_name'}} ) |
|---|
| 396 | | { |
|---|
| 397 | | $DimCnt += 1; |
|---|
| 398 | | #If the dimension is time, let's get the time data for the current slot. |
|---|
| 399 | | #WE get the converted time data instead of using the UTC that is in the time field in the netcdf file. |
|---|
| 400 | | # if( $strDimName =~ 'time' ) |
|---|
| 401 | | # { |
|---|
| 402 | | # $TimeVal = $strTimeVal; |
|---|
| 403 | | # } |
|---|
| 404 | | # elsif( $strDimName =~ 'z' ) |
|---|
| 405 | | # { |
|---|
| 406 | | # $iZDimCnt = $refVarData->{'var_name'}{$strVarName}{'dim_name'}{$strDimName}{'dim_size'}; |
|---|
| 407 | | # } |
|---|
| 408 | | } |
|---|
| 409 | | #Calc the data index if the variable has a Z dim. For instance we have a variable with a Z dim size of |
|---|
| 410 | | #19. First pass $iTimeNdx = 0 and Z dim = 19, so our array offset in the data is iTimeNdx * Z DimSize = 0; |
|---|
| 411 | | #Next time through iTimeNdx = 1 so 1 * 19 = 19, we will pull data starting at slot 19 for the data variable. |
|---|
| 412 | | #If we have a Z dimension for the variable, we need to adjust our array index. |
|---|
| 413 | | #if( $iZDimCnt ) |
|---|
| 414 | | #{ |
|---|
| 415 | | # $iDataNdx *= $iZDimCnt; |
|---|
| 416 | | # $iDataCnt = $iZDimCnt; |
|---|
| 417 | | #} |
|---|
| 418 | | #No Z/height dimension, so we only have point data. |
|---|
| 419 | | #else |
|---|
| 420 | | #{ |
|---|
| 421 | | # $iDataCnt = 1; |
|---|
| 422 | | #} |
|---|
| 423 | | $iDataCnt = 1; |
|---|
| 424 | | my @Data = $refVarData->{'var_name'}{$strVarName}{'data'}; |
|---|
| 425 | | #Get "missing value" value. |
|---|
| 426 | | my $missingVal = $refVarData->{'var_name'}{$strVarName}{'missing_value'}; |
|---|
| 427 | | |
|---|
| 428 | | my $iStartNdx = $iDataNdx; |
|---|
| 429 | | my $iNdx; |
|---|
| 430 | | for( $iNdx = 0; $iNdx < $iDataCnt; $iNdx++ ) |
|---|
| 431 | | { |
|---|
| 432 | | my $Val = @{$Data[0]}[$iStartNdx]; |
|---|
| 433 | | if( $Val != $missingVal ) |
|---|
| 434 | | { |
|---|
| 435 | | $Val = sprintf( '%.3f', $Val ); |
|---|
| 436 | | } |
|---|
| 437 | | #$Val is missing, so we reset it to NULL. |
|---|
| 438 | | else |
|---|
| 439 | | { |
|---|
| 440 | | $Val = 'NULL'; |
|---|
| 441 | | } |
|---|
| 442 | | my $iSOrder = 1; |
|---|
| 443 | | my $obsName = convertToXeniaObsName( $strVarName ); |
|---|
| 444 | | netcdfFuncs::ncOutputData( $strPlatformID, |
|---|
| 445 | | $obsName, |
|---|
| 446 | | $Val, |
|---|
| 447 | | $refVarData->{'var_name'}{$strVarName}{'units'}, |
|---|
| 448 | | $iSOrder, |
|---|
| 449 | | 0, |
|---|
| 450 | | $strTimeVal, |
|---|
| 451 | | $LatVal, |
|---|
| 452 | | $LonVal, |
|---|
| 453 | | $rObsHash ); |
|---|
| 454 | | $iStartNdx++; |
|---|
| 455 | | } |
|---|
| 456 | | } |
|---|
| 457 | | |
|---|
| 458 | | sub isInIgnoreList#(/@ignoreList, $varName) |
|---|
| 459 | | { |
|---|
| 460 | | my( $ignoreList, $varName ) = @_; |
|---|
| 461 | | foreach my $var ( @$ignoreList ) |
|---|
| 462 | | { |
|---|
| 463 | | if( $var eq $varName ) |
|---|
| 464 | | { |
|---|
| 465 | | return(1); |
|---|
| 466 | | } |
|---|
| 467 | | } |
|---|
| 468 | | return(0); |
|---|
| 469 | | } |
|---|
| 470 | | sub convertToXeniaObsName#(obsName) |
|---|
| 471 | | { |
|---|
| 472 | | my( $obsName ) = @_; |
|---|
| 473 | | my $xeniaName = $obsName; |
|---|
| 474 | | if( $obsName eq "sea temperature" ) |
|---|
| 475 | | { |
|---|
| 476 | | $xeniaName = 'water_temperature'; |
|---|
| 477 | | } |
|---|
| 478 | | elsif( $obsName eq 'relative humidity') |
|---|
| 479 | | { |
|---|
| 480 | | return( 'relative_humidity' ); |
|---|
| 481 | | } |
|---|
| 482 | | elsif( $obsName eq 'air temperature') |
|---|
| 483 | | { |
|---|
| 484 | | return( 'air_temperature' ); |
|---|
| 485 | | } |
|---|
| 486 | | elsif( $obsName eq 'atmospheric pressure') |
|---|
| 487 | | { |
|---|
| 488 | | return( 'air_pressure' ); |
|---|
| 489 | | } |
|---|
| 490 | | elsif( $obsName eq 'atmospheric pressure') |
|---|
| 491 | | { |
|---|
| 492 | | return( 'air_pressure' ); |
|---|
| 493 | | } |
|---|
| 494 | | elsif( $obsName eq 'earth relative wind speed') |
|---|
| 495 | | { |
|---|
| 496 | | return( 'wind_speed' ); |
|---|
| 497 | | } |
|---|
| 498 | | elsif( $obsName eq 'earth relative wind direction') |
|---|
| 499 | | { |
|---|
| 500 | | return( 'wind_from_direction' ); |
|---|
| 501 | | } |
|---|
| 502 | | return( $xeniaName ); |
|---|
| 503 | | } |
|---|
| 504 | | |
|---|
| 505 | | sub getDateTimeFromEpoch#( ) |
|---|
| 506 | | { |
|---|
| 507 | | my ( $TimeRef, $Ndx ) = @_; |
|---|
| 508 | | |
|---|
| 509 | | my $strUnits = %$TimeRef->{'var_name'}{'time'}{'units'}; |
|---|
| 510 | | my @Data = %$TimeRef->{'var_name'}{'time'}{'data'}; |
|---|
| 511 | | #COnvert the utc time into the format we use for the database and KML files. |
|---|
| 512 | | my $secsOffset = 315532800; #This is to deal with the fact PCs use a utc from 1980-1-1 instead of the unix 1970-1-1 |
|---|
| 513 | | my $Date = @{$Data[0]}[$Ndx]; |
|---|
| 514 | | my $dateString = strftime( '%Y-%m-%dT%H:%M:%S', gmtime((($Date*60)+$secsOffset)) ); |
|---|
| 515 | | return( $dateString ); |
|---|
| 516 | | } |
|---|