All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.dt.point.decode.MetarParseReport Maven / Gradle / Ivy

Go to download

The NetCDF-Java Library is a Java interface to NetCDF files, as well as to many other types of scientific data formats.

The newest version!
/*
 * Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
 *
 * Portions of this software were developed by the Unidata Program at the
 * University Corporation for Atmospheric Research.
 *
 * Access and use of this software shall impose the following obligations
 * and understandings on the user. The user is granted the right, without
 * any fee or cost, to use, copy, modify, alter, enhance and distribute
 * this software, and any derivative works thereof, and its supporting
 * documentation for any purpose whatsoever, provided that this entire
 * notice appears in all copies of the software, derivative works and
 * supporting documentation.  Further, UCAR requests that the user credit
 * UCAR/Unidata in any publications that result from the use of this
 * software or in any product that includes this software. The names UCAR
 * and/or Unidata, however, may not be used in any advertising or publicity
 * to endorse or promote any products or commercial entity unless specific
 * written permission is obtained from UCAR/Unidata. The user also
 * understands that UCAR/Unidata is not obligated to provide the user with
 * any support, consulting, training or assistance of any kind with regard
 * to the use, operation and performance of this software nor to provide
 * the user with any updates, revisions, new versions or "bug fixes."
 *
 * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * MetarParseReport
 *
 * parses one METAR report into it's variables
 *
 */
package ucar.nc2.dt.point.decode;

import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @deprecated
 */

public class MetarParseReport {

    /**
     * Used to store fields values
    **/
    //private LinkedHashMap field = new LinkedHashMap();
    private LinkedHashMap field = new LinkedHashMap();

    /**
     * Used to store fields units
    **/
    //private HashMap unit = new HashMap();
    private HashMap unit = new HashMap();

    public boolean parseReport(String input) {

        field.clear();
        unit.clear();
        Matcher m;
        float var1, var2, var3;
        String report = null, remark = null;
        String [] split;

        //	return if( /^\n/ ) ;
        if (MP.B_CR.matcher(input).find()) {
            return false;
        }

        // 	split input into report and remark to distinguish fields
        split = MP.REMARKS.split(input);
        report = split[ 0 ] +" ";
        if( split.length ==  2)
            remark = " "+ split[ 1 ] +" ";

        //	$rep_type = $1 if( s#^(METAR|SPECI|TESTM|TESTS) ## ) ;
        m = MP.B_metar.matcher(report);
        if (m.find()) {
            field.put("Report_Type", m.group(1));
            report = m.replaceFirst("");
        } else { // default
            field.put("Report_Type", "METAR");
        }
        unit.put("Report_Type", "" );

        //	$stn_name = $1 if( s#^(\w4) ## ) ;
        m = MP.station.matcher(report);
        if (m.find()) {
            field.put("Station", m.group(1));
            unit.put("Station", "" );
            report = m.replaceFirst(" ");
        } else {
            return false;
        }

        // all patterns from this point are embeded in spaces

        // get day hour minute
        //	if( s#(\d2)(\d2)(\d2)Z## )
        m = MP.ddhhmmZ.matcher(report);
        if (m.find()) {
            field.put("Day", m.group(1));
            unit.put("Day", "" );
            field.put("Hour", m.group(2));
            unit.put("Hour", "" );
            field.put("Minute", m.group(3));
            unit.put("Minute", "" );
            report = m.replaceFirst(" ");
        } else {
            return false;
        }

        // skip NIL reports
        if (MP.NIL.matcher(report).find()) {
            return false;
        }

        m = MP.COR.matcher(report);
        if (m.find()) {
            report = m.replaceFirst(" ");
        }

        //	$AUTO = 1 if( s#AUTO\s+## ) ;
        m = MP.AUTOS.matcher(report);
        if (m.find()) {
            field.put("AUTOS", "yes");
            unit.put("AUTOS", "" );
            report = m.replaceFirst(" ");
        }

        // get wind direction and speed
        //	if( s#(E|W|N|S)?(\d3|VRB)(\d2,3)(G)?(\d2,3)?(KMH|KT|MPS|MPH)\s+## )
        m = MP.wind_direction_speed.matcher(report);
        if (m.find()) {
            //	if( $2 eq "VRB" )
            if (m.group(2).equals("VRB")) {
                if( m.group(1) == null ) {
                    field.put("Variable_Wind_direction", "" );
                } else {
                    field.put("Variable_Wind_direction", m.group(1));
                }
                unit.put("Variable_Wind_direction", "" );
            } else {
                // $DIR = $2 ;
                field.put("Wind_Direction", m.group(2));
                unit.put("Wind_Direction", "degrees" );
            }
            //	$SPD = $3 ;
            field.put("Wind_Speed", m.group(3));
            //	$GUST = $5 if( $4 eq "G" ) ;
            if (m.group(4) != null && m.group(4).equals("G")) {
                field.put("Wind_Gust", m.group(5));
            }
            //		$UNITS = $6 ; need work  if units != KT
            unit.put("Wind_Speed", m.group(6));
            unit.put("Wind_Gust", m.group(6));
            report = m.replaceFirst(" ");
        }
        // get min|max wind direction
        //	if( s#^(\d3)V(\d3)\s+## )
        m = MP.min_max_wind_dir.matcher(report);
        if (m.find()) {
            //		$DIRmin = $1 ;
            field.put("Wind_Direction_Min", m.group(1));
            unit.put("Wind_Direction_Min", "degrees" );
            //		$DIRmax = $2 ;
            field.put("Wind_Direction_Max", m.group(2));
            unit.put("Wind_Direction_Max", "degrees" );
            report = m.replaceFirst(" ");
        }

        // some reports use a place holder for visibility
        //		s#9999\s+## ;
        report = MP.N9999.matcher(report).replaceFirst(" ");

        // get visibility
        boolean done = false;

        //	$prevail_VIS_SM = 0.0 if( s#M1/4SM\s+|<1/4SM\s+## ) ;
        m = MP.visibilitySM.matcher(report);
        if (m.find()) {
            field.put("Visibility", "0.0");
            unit.put("Visibility", "miles");
            report = m.replaceFirst(" ");
            done = true;
        }

        //	$prevail_VIS_KM = 0.0 if( s#M1/4KM\s+|<1/4KM\s+## ) ;
        if( ! done ) {
            
            m = MP.visibilityKM.matcher(report);
            if (m.find()) {
                field.put("Visibility", "0.0");
                unit.put("Visibility", "kilometer");
                report = m.replaceFirst(" ");
                done = true;

            }

        }

        //	if( s#(\d1,4) (\d1,3)/(\d1,3)(SM|KM)\s+## )
        if( ! done ) {

            m = MP.visibility1.matcher(report);
            if (m.find()) {
                //	$prevail_VIS_SM = $1 + ( $2 / $3 ) if( $4 eq "SM" ) ;
                var1 = Float.parseFloat(m.group(1));
                var2 = Float.parseFloat(m.group(2));
                var3 = Float.parseFloat(m.group(3));
                var1 = var1 + (var2 / var3);
                if (m.group(4).equals("SM")) {
                    field.put("Visibility", Float.toString(var1));
                    unit.put("Visibility", "miles");
                    // $prevail_VIS_KM = $1 + ( $2 / $3 ) if( $4 eq "KM" ) ;
                } else {
                    field.put("Visibility", Float.toString(var1));
                    unit.put("Visibility", "kilometer");
                }
                report = m.replaceFirst(" ");
                done = true;
            }

        }
        //	 else( s#^(\d1,3)/(\d1,3)(KM|SM)\s+## )
        if( ! done ) {

            m = MP.visibility2.matcher(report);
            if (m.find()) {
                var1 = Float.parseFloat(m.group(1));
                var2 = Float.parseFloat(m.group(2));
                var1 = var1 / var2;
                //	 $prevail_VIS_SM = $1 / $2  if( $3 eq "SM" ) ;
                if (m.group(3).equals("SM")) {
                    field.put("Visibility", Float.toString(var1));
                    unit.put("Visibility", "miles");
			
                //	 $prevail_VIS_KM = $1 / $2  if( $3 eq "KM" ) ;
                } else {
                    field.put("Visibility", Float.toString(var1));
                    unit.put("Visibility", "kilometer");
                }
                report = m.replaceFirst(" ");
                done = true;
            }
        }
        //	else( s# P?(\d1,4)(KM|SM)\s+## )
        if( ! done ) {
              m = MP.visibility3.matcher(report);
              if (m.find()) {
                  // $prevail_VIS_SM = $1 if( $2 eq "SM" ) ;
                  if (m.group(2).equals("SM")) {
                      field.put("Visibility", m.group(1));
                      unit.put("Visibility", "miles");
                     // $prevail_VIS_KM = $1 if( $2 eq "KM" ) ;
                  } else {
                      field.put("Visibility", m.group(1));
                      unit.put("Visibility", "kilometer");
                  }
                  report = m.replaceFirst(" ");
                  done = true;
              }
        }
        if( ! done ) {
            //	else( s# (\d4)((NE)|(NW)|(SE)|(SW)|(N)|(S)|(E)|(W))\s+## )
            m = MP.visibility_direction.matcher(report);
            if (m.find()) {
            //	$prevail_VIS_M = $1 ;
                field.put("Visibility", m.group(1));
                unit.put("Visibility", "meters");
                // $VIS_dir = $2 ;
                field.put("Visibility_Direction", m.group(2));
                unit.put("Visibility_Direction", "");
                report = m.replaceFirst(" ");
                done = true;
             }
        }

        // 	clear air
        //	$CAVOK = 1 if( s#CAVOK\s+## ) ;
        m = MP.CAVOKS.matcher(report);
        if (m.find()) {
            field.put("Clear_Air", "yes");
            unit.put("Clear_Air", "" );
            report = m.replaceFirst(" ");
        }

        // 	runway decoding here
        //	$RVRNO = 1 if( s#RVRNO\s+## ) ;
        m = MP.RVRNO.matcher(report);
        if (m.find()) {
            //field.put("RVRNOS", "");
            field.put("RunwayReports", "No");
            //unit.put("RVRNOS", "");
            unit.put("RunwayReports", "");
            report = m.replaceFirst(" ");
        }

        for (int i = 0; i < 4; i++) {
            //	if( s# R(\d2)(R|L|C)?/(M|P)?(\d1,4)V?(M|P)?(\d1,4)?(FT|N|D)?\s+## )
            m = MP.runway.matcher(report);
            if (m.find()) {
                //	$RV_designator[ $i ] = "$1$2" ;
                String RV = "RV" + Integer.toString(i +1);
                field.put(RV, m.group(1) );
                unit.put(RV, "");
                /*
                field.put(RV, m.group(1) + m.group(2));
                unit.put(RV, "");

                //	 $RV_above_max[ $i ] = 1
                // if( $3 eq "P" || $5 eq "P" ) ;
                if ((m.group(3) != null && m.group(3).equals("P")) || 
                    (m.group(5) != null && m.group(5).equals("P"))) {
                    field.put(RV + "_Above_Max", "");
                    unit.put(RV + "_Above_Max", "");
                }

                //	$RV_below_min[ $i ] = 1
                //	if( $3 eq "M" || $5 eq "M" ) ;
                if ((m.group(3) != null && m.group(3).equals("M")) || 
                    (m.group(5) != null && m.group(5).equals("M"))) {
                    field.put(RV + "_Below_Min", "");
                    unit.put(RV + "_Below_Min", "");
                }

                //			$RV_vrbl[ $i ] = 1 if( $6 ne "" ) ;
                //			if( $RV_vrbl[ $i ] )
                //				$RV_min[ $i ] = $4 * 1;
                //				$RV_max[ $i ] = $6 * 1;
                if (m.group(6) != null) {
                    field.put(RV + "_Vrbl", "");
                    unit.put(RV + "_Vrbl", "");
                    field.put(RV + "_Min", m.group(4));
                    unit.put(RV + "_Min", "feet");
                    field.put(RV + "_Max", m.group(6));
                    unit.put(RV + "_Max", "feet");
                } else {
                    //				$RV_visRange[ $i ] = $4 * 1;
                    if(m.group(7) != null && m.group(7).equals( "FT")) {
                        field.put(RV + "_Visibility_Range", m.group(4));
                        unit.put(RV + "_Visibility_Range", "feet");
                    } else {
                        field.put(RV + "_Visibility_Range", m.group(4));
                        unit.put(RV + "_Visibility_Range", "");
                    }
                }
                */
                report = m.replaceFirst(" ");
            } else {
                break;
            }
        } // end runway decoding

        // 	Get weather conditions
        // 	code table 4678
        done = true;
        StringBuilder WX = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            // if( s#(\+|-|VC|PR| )(MI|BC|DR|BL|SH|TS|FZ)?(DZ|RA|SN|SG|IC|PE|PL|GR|GS|UP)## )
            m = MP.WeatherPrecip.matcher(report);
            //System.out.println( "before if  report ="+ report );
            if (m.find()) {
                done = false;
                //System.out.println( "after if report ="+ report );
                //if (! m.group(1).equals( " " )) {
                    WX.append( m.group(1) );
                //}
                if (m.group(2) != null) {
                    WX.append( m.group(2) );
                }
                WX.append( m.group(3) );
                report = m.replaceFirst(" ");
            }
            //  if( s#(\+|-|VC|PR| )(MI|BC|DR|BL|SH|TS|FZ)?(BR|FG|FU|VA|DU|SA|HZ|PY)## )
            m = MP.WeatherObs.matcher(report);
            if (m.find()) {
                done = false;
                //System.out.println( "after if report ="+ report );
                //if (! m.group(1).equals( " " )) {
                    WX.append( m.group(1) );
                //}
                if (m.group(2) != null) {
                    WX.append( m.group(2) );
                }
                WX.append( m.group(3) );
                report = m.replaceFirst(" ");
            }
            // if( s#(\+|-|VC|PR| )(MI|BC|DR|BL|SH|TS|FZ)?(PO|SQ|FC|SS|DS)## )
            m = MP.WeatherOther.matcher(report);
            if (m.find()) {
                done = false;
                //System.out.println( "after if report ="+ report );
                //if (! m.group(1).equals( " " )) {
                    WX.append( m.group(1) );
                //}
                if (m.group(2) != null) {
                    WX.append( m.group(2) );
                }
                WX.append( m.group(3) );
                report = m.replaceFirst(" ");
            }
            if( done )
                break;
            done = true;
            WX.append( " " );
        }
        if (WX.length() > 0) {
            WX.setLength( WX.length() -1 );
            field.put("Weather", WX.toString() );
            unit.put("Weather", "");
        }
        //System.out.println( "after if report ="+ report );

        // 	Interpret cloud conditions
        //	$cloud_type[ 0 ] = $1 if( s#(CLR|SKC)\s+## ) ;
        m = MP.CLR_or_SKC.matcher(report);
        if (m.find()) {
            field.put("Cloud_Type", m.group(1));
            unit.put("Cloud_Type", "");
            report = m.replaceFirst(" ");
        }

        //	$vert_VIS = cloud_hgt2_meters( $1 ) if( s#^VV(\d3)\s+## ) ;
        m = MP.vertical_VIS.matcher(report);
        if (m.find()) {
            field.put("Vertical_Visibility", cloud_hgt2_meters(m.group(1)));
            unit.put("Vertical_Visibility", "meters" );
            report = m.replaceFirst(" ");
        }

        // 	cloud layers up to 6
        for (int i = 0; i < 6; i++) {
            //		if( s# (\+|-)?(OVC|SCT|FEW|BKN)(\d3)(\w1,3)?\s+## )
            m = MP.cloud_cover.matcher(report);
            if (m.find()) {
                String cloud = "Cloud_Layer_" + Integer.toString(i + 1);
                if (m.group(1) == null) {
                    field.put(cloud + "_Type", m.group(2));
                } else {
                    field.put(cloud + "_Type", m.group(1) + m.group(2));
                }
                unit.put(cloud + "_Type", "");
                // just report meters
                // $cloud_hgt[ $i ] = $3 * 100 ;
                //field.put(cloud + "_Height", Integer.toString(Integer.parseInt(m.group(3)) * 100));
                //unit.put(cloud + "_Height", "feet");

                 //	$cloud_meters[ $i ] = cloud_hgt2_meters( $3 ) ;
                field.put(cloud + "_Height", cloud_hgt2_meters(m.group(3)));
                unit.put(cloud + "_Height", "meters");
                //	$cloud_phenom[ $i ] = padstr( $4, 4 ) if( $4 ) ;
                if (m.group(4) != null) {
                    field.put(cloud + "_Phenom", m.group(4));
                    unit.put(cloud + "_Phenom", "");
                }
                report = m.replaceFirst(" ");
            } else {
                break;
            }
        } // end clouds

        // 	Temperature and Dew Point, try temp tenths first
        float air_temperature = -999;
        float dew_point_temperature = -999;
        //	if( s#T(0|1)(\d3)(0|1)?(\d3)?\s+## )
        if( remark != null)
            m = MP.Temperature_tenths.matcher(remark);

        if ( remark != null && m.find() ) {
            //		if( $1 == 0 )
            //			$T_tenths = 0.1 * $2 ;
            //	 	else
            //			$T_tenths = -0.1 * $2 ;
            //
            air_temperature = (float)(Float.parseFloat(m.group(2)) * .1);
            if (m.group(1).equals("1")) {
                //T = "-" + T;
                air_temperature *= -1;
            }
            field.put("Temperature", Float.toString( air_temperature ) );
            unit.put("Temperature", "Celsius");

            //		if( defined( $3 ) && $3 == 0 )
            //			$TD_tenths = 0.1 * $4 ;
            //	 	elsif( defined( $3 ) && $3 == 1 )
            //			$TD_tenths = -0.1 * $4 ;
            //
            if (m.group(3) != null) {
                dew_point_temperature = (float)(Float.parseFloat(m.group(4)) * .1);
                //			$TD *= -1 if( $3 ) ;
                if (m.group(3).equals("1")) {
                    //TD = "-" + TD;
                    dew_point_temperature *= -1;
                }
                field.put("DewPoint", Float.toString( dew_point_temperature ));
                unit.put("DewPoint", "Celsius");
            }
            remark = m.replaceFirst(" ");

        } else { // check for coarse temperature
            // 	get temperature and dew point
            //	if( s#^(M)?(\d2)/(M)?(\d2)?\s+## )
            m = MP.Temperature.matcher(report);
            if (m.find()) {
                //		$T = $2 ;
                //String T = m.group(2);
                air_temperature = Float.parseFloat(m.group(2));
                //		$T *= -1 if( $1 ) ;
                //if(  m.group( 1 ).equals( "M" )  )
                if (m.group(1) != null) {
                    //T = "-" + T;
                    air_temperature *= -1;
                }
                field.put("Temperature", Float.toString( air_temperature ) );
                unit.put("Temperature", "Celsius");
                //		$TD = $4 if( defined( $4 ) ) ;
                if (m.group(4) != null) {
                    dew_point_temperature = Float.parseFloat(m.group(4));
                    //			$TD *= -1 if( $3 ) ;
                    if (m.group(3) != null) {
                        //TD = "-" + TD;
                       dew_point_temperature *= -1;
                    }
                    field.put("DewPoint", Float.toString( dew_point_temperature ));
                    unit.put("DewPoint", "Celsius");
                }
                report = m.replaceFirst(" ");
            } // end T and TD
        }

        // 	get Altimeter settings
        //	if( s# (A|Q)(\d4\.?\d?)\s+## )
        m = MP.altimeter.matcher(report);
        if (m.find()) {
            // if( $1 eq "A" )
            if (m.group(1).equals("A")) {
                // $inches_ALTIM = $2 * 0.01 ;
                field.put("Altimeter", Double.toString(Float.parseFloat(m.group(2)) * 0.01));
                unit.put("Altimeter", "inches");
            } else {
                // $hectoPasc_ALTIM = $2 ;
                field.put("Altimeter", m.group(2));
                unit.put("Altimeter", "hectopascal");
            }
            report = m.replaceFirst(" ");
        }

        //	$NOSIG = 1 if( s#NOSIG## ) ;
        m = MP.NOSIG.matcher(report);
        if (m.find()) {
            field.put("Weather", "No");
            unit.put("Weather", "");
            report = m.replaceFirst(" ");
        }


        // 	check for remarks or done
        if( remark == null)
            return true;

        // process remarks now, looking for most used ones first

        // get Automated reports
        //	 if( s#(A01|A01A|A02|A02A|AO1|AO1A|AO2|AO2A|AOA)\s+## ) ;
        m = MP.automatic_report.matcher(remark);
        if (m.find()) {
            field.put("Automatic_Report", m.group(1));
            unit.put("Automatic_Report", "");
            remark = m.replaceFirst(" ");
        }

        //	check if no more info in report
        if (MP.spaces.matcher(remark).matches()) {
            return true;
        }

        // Sea-Level presure not available
        //$SLPNO = 1 if( s# SLPNO\s+## ) ;
        m = MP.SLPNO.matcher(remark);
        if (m.find()) {
            //field.put("SLPNO", "yes");
            //unit.put("SLPNO", "");
            field.put("Sea_Level_Pressure_available", "No");
            unit.put("Sea_Level_Pressure_available", "");            
            remark = m.replaceFirst(" ");
        }

        //if( s# SLP\s?(\d3)\s+## )
        m = MP.SLP.matcher(remark);
        if (m.find()) {
            float slp;
            //	if( $1 >= 550 )
            if (Integer.parseInt(m.group(1)) >= 550) {
                // $SLP = $1 / 10. + 900. ;
                field.put("Sea_Level_Pressure", Double.toString(Float.parseFloat(m.group(1)) * 0.1 + 900));
            } else {
                // $SLP =  $1 / 10. + 1000. ;
                field.put("Sea_Level_Pressure", Double.toString(Float.parseFloat(m.group(1)) * 0.1 + 1000));
            }
            unit.put("Sea_Level_Pressure", "hectopascal");
            remark = m.replaceFirst(" ");
        }
        //	check if no more info in report
        if (MP.spaces.matcher(remark).matches()) {
            return true;
        }

        // 	Hourly precipitation amount
        //	$PRECIP_hourly = $1 / 100 if( s#P ?(\d1,5)\s+## ) ;
        m = MP.hourly_precip.matcher(remark);
        if (m.find()) {
            field.put("Hourly_Precipitation", Double.toString(Float.parseFloat(m.group(1)) * .01));
            unit.put("Hourly_Precipitation", "inches");
            remark = m.replaceFirst(" ");
        }

        //	check if no more info in report
        if (MP.spaces.matcher(remark).matches()) {
            return true;
        }

        // precipitation sensor not working  PWINO
        //$PWINO = 1 if( s#PWINO\s+## ) ;
        m = MP.PWINO.matcher(remark);
        if (m.find()) {
            //field.put("PWINO", "");
            //unit.put("PWINO", "");
            field.put("PRECIP_sensor_working", "No");
            unit.put("PRECIP_sensor_working", "");
            remark = m.replaceFirst(" ");
        }

        //	check if no more info in report
        if (MP.spaces.matcher(remark).matches()) {
            return true;
        }

        // 	Lightning detection sensor not working  TSNO
        //	$TSNO = 1 if( s#TSNO\s+## ) ;
        m = MP.TSNO.matcher(remark);
        if (m.find()) {
            //field.put("TSNO", "yes");
            //unit.put("TSNO", "");
            field.put("Lightning_sensor_working", "No");
            unit.put("Lightning_sensor_working", "");
            remark = m.replaceFirst(" ");
        }

        //	check if no more info in report
        if (MP.spaces.matcher(remark).matches()) {
            return true;
        }

        // 	get Tornado data if present
        //	if( s#(TORNADO\w0,2|WATERSPOUTS*|FUNNEL CLOUDS*)\s+## )
        m = MP.tornado.matcher(remark);
        if (m.find()) {
            field.put("TornadicType", m.group(1));
            unit.put("TornadicType", "");
            remark = m.replaceFirst(" ");
            // if( s#(B|E)(\d\d)(\d\d)?\s+## )
            m = MP.tornadoTime.matcher(remark);
            if (m.find()) {
                String time;
                String units;
                if(m.group(2) == null ) {
                    time = m.group(3);
                    units = "mm";
                } else {
                    time = m.group(2) + m.group(3);
                    units = "hhmm";
                }
                // if( $1 eq "B" )
                if (m.group(1).equals("B")) {
                    // $BTornadic_hh = $2 ;
                    // $BTornadic_mm = $3 ;
                    field.put("Begin_Tornado", time);
                    unit.put("Begin_Tornado", units);
                } else {
                     // $ETornadic_hh = $2 ;
                     //	 $ETornadic_mm = $3 if( defined( $3 ) ) ;
                    field.put("End_Tornado", time);
                    unit.put("End_Tornado", units);
                }
                remark = m.replaceFirst(" ");
            }
            // $TornadicLOC = padstr( $1, 10 )
            //	 if( s#^(DSNT|VCY STN|VC STN|VCY|VC)\s+## ) ;
            m = MP.tornadoLocation.matcher(remark);
            if (m.find()) {
                field.put("Tornado_Location", m.group(1));
                unit.put("Tornado_Location", "");
                remark = m.replaceFirst(" ");
            }

             // $TornadicDIR = padstr( $1, 2 )
            //	if( s#^(NE|NW|SE|SW|N|S|E|W)\s+## ) ;
            m = MP.tornadoDirection.matcher(remark);
            if (m.find()) {
                field.put("Tornado_Direction", m.group(1));
                unit.put("Tornado_Direction", "");
                remark = m.replaceFirst(" ");
            }
        } // end tornado

        // 	get Peak winds
        //	if( s#PK WND (\d3)(\d1,3)/(\d\d)?(\d\d)\s+## )
        m = MP.peakWind.matcher(remark);
        if (m.find()) {
            // $PKWND_dir = $1 ;
            field.put("Peak_Wind_Direction", m.group(1));
            unit.put("Peak_Wind_Direction", "degrees");
            // $PKWND_spd = $2 ;
            field.put("Peak_Wind_Speed", m.group(2));
            unit.put("Peak_Wind_Speed", "knots");
            // $PKWND_hh = $3 if( defined( $3 ) ) ;
            // $PKWND_mm = $4 ;
            String time;
            String units;
            if(m.group(3) == null ) {
                 time = m.group(4);
                 units = "mm";
            } else {
                 time = m.group(3) + m.group(4);
                 units = "hhmm";
            }
            field.put("Peak_Wind_Time", time );
            unit.put("Peak_Wind_Time", units );
            remark = m.replaceFirst(" ");
        }

        // 	get Wind shift
        //	if( s#WSHFT (\d\d)?(\d\d)\s+## )
        m = MP.windShift.matcher(remark);
        if (m.find()) {
            // $WshfTime_hh = $1 if( defined( $1 ) );
            // $WshfTime_mm = $2 ;
            String time;
            String units;
            if(m.group(1) == null ) {
                 time = m.group(2);
                 units = "mm";
            } else {
                 time = m.group(1) + m.group(2);
                 units = "hhmm";
            }

            field.put("Wind_Shift", time);
            unit.put("Wind_Shift", units);
            remark = m.replaceFirst(" ");
        }

        // 	get FROPO ( wind shift because of frontal passage )
        //	$Wshft_FROPA = 1 if( s#FROPA\s+## ) ;
        m = MP.FROPA.matcher(remark);
        if (m.find()) {
            field.put("Wind_Shift_Frontal_Passage", "Yes");
            unit.put("Wind_Shift_Frontal_Passage", "");
            remark = m.replaceFirst(" ");
        }

        // 	Tower visibility
        //	if( s#TWR (VIS|VSBY) (\d1,3) (\d1,2)/(\d1,2)\s+## )
        //	$VIS_TWR = $2 + ( $3 / $4 ) ;
        m = MP.towerVisibility1.matcher(remark);
        if (m.find()) {
            var1 = Float.parseFloat(m.group(2));
            var2 = Float.parseFloat(m.group(3));
            var3 = Float.parseFloat(m.group(4));
            var1 = var1 + (var2 / var3);
            field.put("Tower_Visibility", Float.toString(var1));
            unit.put("Tower_Visibility", "miles");
            remark = m.replaceFirst(" ");
            // 	elsif( s#TWR (VIS|VSBY) (\d1,2)/(\d1,2)\s+## )
            //		$VIS_TWR = ( $2 / $3 ) ;
        } else {
            m = MP.towerVisibility2.matcher(remark);
            if (m.find()) {
                var1 = Float.parseFloat(m.group(2));
                var2 = Float.parseFloat(m.group(3));
                var1 = var1 / var2;
                field.put("Tower_Visibility", Float.toString(var1));
                unit.put("Tower_Visibility", "miles");
                remark = m.replaceFirst(" ");
                // 		elsif( s#TWR (VIS|VSBY) (\d1,3)\s+## )
                //			$VIS_TWR = $2 ;
            } else {
                m = MP.towerVisibility3.matcher(remark);
                if (m.find()) {
                    field.put("Tower_Visibility", m.group(2));
                    unit.put("Tower_Visibility", "miles");
                    remark = m.replaceFirst(" ");
                }
            }
        }
        
        // Surface visibility
        //	if( s# SFC (VIS|VSBY) (\d1,3) (\d1,2)/(\d1,2)\s+## )
        //	$VIS_SFC = $2 + ( $3 / $4 ) ;
        m = MP.surfaceVisibility1.matcher(remark);
        if (m.find()) {
            var1 = Float.parseFloat(m.group(2));
            var2 = Float.parseFloat(m.group(3));
            var3 = Float.parseFloat(m.group(4));
            var1 = var1 + (var2 / var3);
            field.put("Surface_Visibility", Float.toString(var1));
            unit.put("Surface_Visibility", "miles");
            remark = m.replaceFirst(" ");
            // 		elsif( s# SFC (VIS|VSBY) (\d1,2)/(\d1,2)\s+## )
            //			$VIS_SFC = ( $2 / $3 ) ;
        } else {
            m = MP.surfaceVisibility2.matcher(remark);
            if (m.find()) {
                var1 = Float.parseFloat(m.group(2));
                var2 = Float.parseFloat(m.group(3));
                var1 = var1 / var2;
                field.put("Surface_Visibility", Float.toString(var1));
                unit.put("Surface_Visibility", "miles");
                remark = m.replaceFirst(" ");
                //  elsif( s# SFC (VIS|VSBY) (\d1,3)\s+## ) 
                // $VIS_SFC = $2 ;
            } else {
                m = MP.surfaceVisibility3.matcher(remark);
                if (m.find()) {
                    field.put("Surface_Visibility", m.group(2));
                    unit.put("Surface_Visibility", "miles");
                    remark = m.replaceFirst(" ");
                }
            }
        }

        // 	Variable visibility
        //	if( s#(VIS|VSBY) (\d1,3) (\d1,2)/(\d1,2)V(\d1,3) (\d1,2)/(\d1,2)\s+## )
        //		$VISmin = $2 + ( $3 / $4 ) ;
        //		$VISmax = $5 + ( $6 / $7 ) ;
        m = MP.variableVisibility1.matcher(remark);
        if (m.find()) {
            var1 = Float.parseFloat(m.group(2));
            var2 = Float.parseFloat(m.group(3));
            var3 = Float.parseFloat(m.group(4));
            var1 = var1 + (var2 / var3);
            field.put("Variable_Visibility_Min", Float.toString(var1));
            unit.put("Variable_Visibility_Min", "miles");
            var1 = Float.parseFloat(m.group(5));
            var2 = Float.parseFloat(m.group(6));
            var3 = Float.parseFloat(m.group(7));
            var1 = var1 + (var2 / var3);
            field.put("Variable_Visibility_Max", Float.toString(var1));
            unit.put("Variable_Visibility_Max", "miles");
            remark = m.replaceFirst(" ");
            //	 elsif( s#(VIS|VSBY) (\d1,3)V(\d1,3) (\d1,2)/(\d1,2)\s+## )
            //		$VISmin = $2 ;
            //		$VISmax = $3 + ( $4 / $5 ) ;
        } else {
            m = MP.variableVisibility2.matcher(remark);
            if (m.find()) {
                field.put("Variable_Visibility_Min", m.group(2));
                unit.put("Variable_Visibility_Min", "miles");
                var1 = Float.parseFloat(m.group(3));
                var2 = Float.parseFloat(m.group(4));
                var3 = Float.parseFloat(m.group(5));
                var1 = var1 + (var2 / var3);
                field.put("Variable_Visibility_Max", Float.toString(var1));
                unit.put("Variable_Visibility_Max", "miles");
                remark = m.replaceFirst(" ");
                //  elsif( s#(VIS|VSBY) (\d1,2)/(\d1,2)V(\d1,3) (\d1,2)/(\d1,2)\s+## )
                //			$VISmin = ( $2 / $3 ) ;
                //			$VISmax = $4 + ( $5 / $6 ) ;
            } else {
                m = MP.variableVisibility3.matcher(remark);
                if (m.find()) {
                    var1 = Float.parseFloat(m.group(2));
                    var2 = Float.parseFloat(m.group(3));
                    var1 = var1 / var2;
                    field.put("Variable_Visibility_Min", Float.toString(var1));
                    unit.put("Variable_Visibility_Min", "miles");
                    var1 = Float.parseFloat(m.group(4));
                    var2 = Float.parseFloat(m.group(5));
                    var3 = Float.parseFloat(m.group(6));
                    var1 = var1 + (var2 / var3);
                    field.put("Variable_Visibility_Max", Float.toString(var1));
                    unit.put("Variable_Visibility_Max", "miles");
                    remark = m.replaceFirst(" ");
                    // elsif( s#(VIS|VSBY) (\d1,3) (\d1,2)/(\d1,2)V(\d1,3)\s+## )
                    //	 $VISmin = $2 + ( $3 / $4 ) ;
                    //	 $VISmax = $5 ;
                } else {
                    m = MP.variableVisibility4.matcher(remark);
                    if (m.find()) {
                        var1 = Float.parseFloat(m.group(2));
                        var2 = Float.parseFloat(m.group(3));
                        var3 = Float.parseFloat(m.group(4));
                        var1 = var1 + (var2 / var3);
                        field.put("Variable_Visibility_Min", Float.toString(var1));
                        unit.put("Variable_Visibility_Min", "miles");
                        field.put("Variable_Visibility_Max", m.group(5));
                        unit.put("Variable_Visibility_Max", "miles");
                        remark = m.replaceFirst(" ");
                        // elsif( s#(VIS|VSBY) (\d1,3)V(\d1,3)\s+## )
                        //	 $VISmin = $2 ;
                        //	 $VISmax = $3 ;
                    } else {
                        m = MP.variableVisibility5.matcher(remark);
                        if (m.find()) {
                            field.put("Variable_Visibility_Min", m.group(2));
                            unit.put("Variable_Visibility_Min", "miles");
                            field.put("Variable_Visibility_Max", m.group(3));
                            unit.put("Variable_Visibility_Max", "miles");
                            remark = m.replaceFirst(" ");
                            // elsif( s#(VIS|VSBY) (\d1,2)/(\d1,2)V(\d1,3)\s+## )
                            //	 $VISmin = ( $2 / $3 ) ;
                            //	 $VISmax = $4 ;
                        } else {
                            m = MP.variableVisibility6.matcher(remark);
                            if (m.find()) {
                                var1 = Float.parseFloat(m.group(2));
                                var2 = Float.parseFloat(m.group(3));
                                var1 = var1 / var2;
                                field.put("Variable_Visibility_Min", Float.toString(var1));
                                unit.put("Variable_Visibility_Min", "miles");
                                field.put("Variable_Visibility_Max", m.group(4));
                                unit.put("Variable_Visibility_Max", "miles");
                                remark = m.replaceFirst(" ");
                            }
                        }
                    }
                }
            }
        } // end variableVisibility

        // 	Second site visiblity
        //	if( s#(VIS|VSBY) (\d1,3) (\d1,2)/(\d1,2) (RY\d1,2)\s+## )
        //		$VIS_2ndSite = $2 + ( $3 / $4 ) ;
        //		$VIS_2ndSite_LOC = padstr( $5, 10 ) ;
        m = MP.Visibility2ndSite1.matcher(remark);
        if (m.find()) {
            var1 = Float.parseFloat(m.group(2));
            var2 = Float.parseFloat(m.group(3));
            var3 = Float.parseFloat(m.group(4));
            var1 = var1 + (var2 / var3);
            field.put("Second_Site_Visibility", Float.toString(var1));
            unit.put("Second_Site_Visibility", "miles");
            field.put("Second_Site_Location", m.group(5));
            unit.put("Second_Site_Location", "");
            remark = m.replaceFirst(" ");
            // 	elsif( s#(VIS|VSBY) (\d1,3) (RY\d1,2)\s+## )
            //		$VIS_2ndSite = $2 ;
            //		$VIS_2ndSite_LOC = padstr( $3, 10 ) ;
        } else {
            m = MP.Visibility2ndSite2.matcher(remark);
            if (m.find()) {
                field.put("Second_Site_Visibility", m.group(2));
                unit.put("Second_Site_Visibility", "miles");
                field.put("Second_Site_Location", m.group(3));
                unit.put("Second_Site_Location", "");
                remark = m.replaceFirst(" ");
                // elsif( s#(VIS|VSBY) (\d1,2)/(\d1,2) (RY\d1,2)\s+## )
                //	 $VIS_2ndSite = ( $2 / $3 ) ;
                //	 $VIS_2ndSite_LOC = padstr( $4, 10 ) ;
            } else {
                m = MP.Visibility2ndSite3.matcher(remark);
                if (m.find()) {
                    var1 = Float.parseFloat(m.group(2));
                    var2 = Float.parseFloat(m.group(3));
                    var1 = var1 / var2;
                    field.put("Second_Site_Visibility", Float.toString(var1));
                    unit.put("Second_Site_Visibility", "miles");
                    field.put("Second_Site_Location", m.group(4));
                    unit.put("Second_Site_Location", "");
                    remark = m.replaceFirst(" ");
                }
            }
        } // end Second site visiblity

/* let lighning go to extra fields cuz convnetion not followed
        // 	Lightning data ( Occasional,Frequent,Continuous) and
        //	(Cloud-Ground,In-Cloud,Cloud-Cloud,Cloud-Air)
        //	if( s# (OCNL|FRQ|CNS) LTG\s?(CG|IC|CC|CA)\s?(DSNT|AP|VCY STN|VCNTY STN)?\s?(NE|NW|SE|SW|N|S|E|W)?\s+## )
        m = MP.Lightning.matcher(remark);
        if (m.find()) {
           //		$LTG_OCNL = 1 if( $1 eq "OCNL" ) ;
           //		$LTG_FRQ = 1 if( $1 eq "FRQ" ) ;
           //		$LTG_CNS = 1 if( $1 eq "CNS" ) ;
           //		$LTG_CG = 1 if( $2 eq "CG" ) ;
           //		$LTG_IC = 1 if( $2 eq "IC" ) ;
           //		$LTG_CC = 1 if( $2 eq "CC" ) ;
           //		$LTG_CA = 1 if( $2 eq "CA" ) ;
           //		$LTG_DSNT = 1 if( $3 eq "DSNT" ) ;
           //		$LTG_AP = 1 if( $3 eq "AP" ) ;
           //		$LTG_VcyStn = 1 if( $3 eq "VCY STN" || $3 eq "VCNTY STN" ) ;
           //		$LTG_DIR = padstr( $4, 2 ) if( defined( $4 ) ) ;
           field.put("Lightning", m.group(1));
           unit.put("Lightning", "");
           remark = m.replaceFirst(" ");
        } // end Lightning data
*/

        // 	get min/max for Variable Ceiling
        //	if( s#CIG (\d1,4)V(\d1,4)\s+## )
        //		$Ceiling_min = $1 ;
        //		$Ceiling_max = $2 ;
        m = MP.CIG.matcher(remark);
        if (m.find()) {
            field.put("Ceiling_Min", Integer.toString(Integer.parseInt(m.group(1)) * 100));
            unit.put("Ceiling_Min", "feet");
            field.put("Ceiling_Max", Integer.toString(Integer.parseInt(m.group(2)) * 100));
          unit.put("Ceiling_Max", "feet");
            remark = m.replaceFirst(" ");
        }

        //	 ? about SKY condition at 2nd location
        // 	get 2nd site ceiling and location
        //	if( s#CIG (\d3) (RY\d1,2)\s+## )
        //		$CIG_2ndSite_meters = $1 * 10 ;
        //		$CIG_2ndSite_LOC = $2 ;
        m = MP.CIG_RY.matcher(remark);
        if (m.find()) {
            var1 = Float.parseFloat(m.group(1)) * 10;
            field.put("Second_Site_Sky", Float.toString(var1));
            unit.put("Second_Site_Sky", "feet");
            field.put("Second_Site_Sky_Location", m.group(2));
            unit.put("Second_Site_Sky_Location", "");
            remark = m.replaceFirst(" ");
        }

        // 	Presure falling rapidly
        //	$PRESFR = 1 if( s#PRESFR/?\s+## ) ;
        m = MP.PRESFR.matcher(remark);
        if (m.find()) {
            field.put("Pressure_Falling_Rapidly", "Yes");
            unit.put("Pressure_Falling_Rapidly", "");
            remark = m.replaceFirst(" ");
        }

        // 	Presure rising rapidly
        //	$PRESRR = 1 if( s#PRESRR/?\s+## ) ;
        m = MP.PRESRR.matcher(remark);
        if (m.find()) {
            field.put("Pressure_Rising_Rapidly", "Yes");
            unit.put("Pressure_Rising_Rapidly", "");
            remark = m.replaceFirst(" ");
        }

        // 	Sector visibility
        //	if( s# (VIS|VSBY) (NE|NW|SE|SW|N|S|E|W)(\d1,3) (\d1,2)/(\d1,2)\s+## )
        //		$SectorVIS_DIR = padstr( $2, 2 ) ;
        //		$SectorVIS = $3 + ( $4 / $5 ) ;
        m = MP.sectorVisibility1.matcher(remark);
        if (m.find()) {
            field.put("Sector_Visibility_Direction", m.group(2));
            unit.put("Sector_Visibility_Direction", "");
            var1 = Float.parseFloat(m.group(3));
            var2 = Float.parseFloat(m.group(4));
            var3 = Float.parseFloat(m.group(5));
            var1 = var1 + (var2 / var3);
            field.put("Sector_Visibility", Float.toString(var1));
            unit.put("Sector_Visibility", "miles");
            remark = m.replaceFirst(" ");
            // 	elsif( s#(VIS|VSBY) (NE|NW|SE|SW|N|S|E|W) (\d1,2)/(\d1,2)\s+## )
            //		$SectorVIS_DIR = padstr( $2, 2 ) ;
            //		$SectorVIS = ( $3 / $4 ) ;
        } else {
            m = MP.sectorVisibility2.matcher(remark);
            if (m.find()) {
                field.put("Sector_Visibility_Direction", m.group(2));
                unit.put("Sector_Visibility_Direction", "");
                var1 = Float.parseFloat(m.group(3));
                var2 = Float.parseFloat(m.group(4));
                var1 = var1 / var2;
                field.put("Sector_Visibility", Float.toString(var1));
                unit.put("Sector_Visibility", "miles");
                remark = m.replaceFirst(" ");
                // 		elsif( s#(VIS|VSBY) (NE|NW|SE|SW|N|S|E|W)(\d1,3)\s+## )
                //			$SectorVIS_DIR = padstr( $2, 2 ) ;
                //			$SectorVIS = $3 ;
            } else {
                m = MP.sectorVisibility3.matcher(remark);
                if (m.find()) {
                    field.put("Sector_Visibility_Direction", m.group(2));
                    unit.put("Sector_Visibility_Direction", "");
                    field.put("Sector_Visibility", m.group(3));
                    unit.put("Sector_Visibility", "miles");
                    remark = m.replaceFirst(" ");
                }
            }
        }

        // 	Hailstone activity and size
        //	if( s# GR M1/4\s+## )
        //		$GR = 1 ;
        //		$GRsize = 1 / 8 ;
        m = MP.GR1.matcher(remark);
        if (m.find()) {
            field.put("Hailstone_Activity", "yes");
            unit.put("Hailstone_Activity", "");
            field.put("Hailstone_Size", "0.25");
            unit.put("Hailstone_Size", "");
            remark = m.replaceFirst(" ");
            // 	elsif( s# GR (\d1,3) (\d1,2)/(\d1,2)\s+## )
            //		$GR = 1 ;
            //		$GRsize = $1 + ( $2 / $3 ) ;
        } else {
            m = MP.GR2.matcher(remark);
            if (m.find()) {
                field.put("Hailstone_Activity", "yes");
                unit.put("Hailstone_Activity", "");
                var1 = Float.parseFloat(m.group(1));
                var2 = Float.parseFloat(m.group(2));
                var3 = Float.parseFloat(m.group(3));
                var1 = var1 + (var2 / var3);
                field.put("Hailstone_Size", Float.toString(var1));
                unit.put("Hailstone_Size", "");
                remark = m.replaceFirst(" ");
                // 		elsif( s# GR (\d1,2)/(\d1,2)\s+## )
                //			$GR = 1 ;
                //			$GRsize = ( $1 / $2 ) ;
            } else {
                m = MP.GR3.matcher(remark);
                if (m.find()) {
                    field.put("Hailstone_Activity", "yes");
                    unit.put("Hailstone_Activity", "");
                    var1 = Float.parseFloat(m.group(1));
                    var2 = Float.parseFloat(m.group(2));
                    var1 = var1 / var2;
                    field.put("Hailstone_Size", Float.toString(var1));
                    unit.put("Hailstone_Size", "");
                    remark = m.replaceFirst(" ");
                    // 			elsif( s#GR (\d1,3)\s+## )
                    //				$GR = 1 ;
                    //				$GRsize = $1 ;
                } else {
                    m = MP.GR4.matcher(remark);
                    if (m.find()) {
                        field.put("Hailstone_Activity", "yes");
                        unit.put("Hailstone_Activity", "");
                        field.put("Hailstone_Size", m.group(1));
                        unit.put("Hailstone_Size", "");
                        remark = m.replaceFirst(" ");
                    }
                }
            }
        }
        //	$GR = 1 if( s# GS\s+## ) ;
        m = MP.GR.matcher(remark);
        if (m.find()) {
            field.put("Hailstone_Activity", "yes");
            unit.put("Hailstone_Activity", "");
            remark = m.replaceFirst(" ");
        }

        // 	VIRGA activity
        //	if( s#VIRGA (DSNT )?(NE|NW|SE|SW|N|S|E|W)?\s+## )
        //		$VIRGA = 1 ;
        //		$VIRGAdir = padstr( $2, 2 ) if( $2 ) ;
        m = MP.VIRGA.matcher(remark);
        if (m.find()) {
            field.put("Virga_Activity", "yes");
            unit.put("Virga_Activity", "");
            if( m.group(1) != null ) {
                field.put("Virga_Direction", m.group(1));
                unit.put("Virga_Direction", "");
            }
            if( m.group(2) != null ) {
                field.put("Virga_Direction", m.group(2));
                unit.put("Virga_Direction", "");
            }
            remark = m.replaceFirst(" ");
        }

        // 	Surface-based Obscuring Phenomena  SfcObscuration weather conditions
        // 	code table 4678
        //	if( s#-X(VC|PR)?(MI|BC|DR|BL|SH|TS|FZ)?(DZ|RA|SN|SG|IC|PE|PL|GR|GS|UP)?(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS|DS)?(\d)\s+## )
        //		$SfcObscuration = padstr( "$1$2$3$4$5", 8 ) ;
        //		$OctsSkyObscured = $6 ;
        /* not coded to current implementation, so let it go to plain lang remarks
        m = MP.obscuring.matcher(remark);
        if (m.find()) {
            String tmp = "";
            if (m.group(1) != null) {
                tmp = m.group(1);
            }
            if (m.group(2) != null) {
                tmp = m.group(2);
            }
            if (m.group(3) != null) {
                tmp = m.group(3);
            }
            if (m.group(4) != null) {
                tmp = m.group(4);
            }
            if (m.group(5) != null) {
                tmp = m.group(5);
            }
            if ( ! tmp.equals("")) {
                field.put("Surface_Obscuration", tmp );
                unit.put("Surface_Obscuration", "" );
            }
            field.put("OctsSkyObscured", m.group(6));
            unit.put("OctsSkyObscured", "");
            remark = m.replaceFirst(" ");
        }
        */

        // 	get Ceiling_est or Ceiling height
        //	$CIGNO = 1 if( s#CIGNO\s+## ) ;
        m = MP.CIGNO.matcher(remark);
        if (m.find()) {
            //field.put("CIGNO", "");
            //unit.put("CIGNO", "");
            field.put("Ceiling_height_available", "No");
            unit.put("Ceiling_height_available", "");            
            remark = m.replaceFirst(" ");
        }

        //	if( s#CIG(E)?(\d3)\s+## )
        //		if( $1 eq "E" )
        //			$Ceiling_est = $2 * 100 ;
        //		 else
        //			$Ceiling = $2 * 100 ;
        m = MP.CIG_EST.matcher(remark);
        if (m.find()) {
            String est = Integer.toString(Integer.parseInt(m.group(2)) * 100);
            //if (m.group(1).equals("E")) {
                //field.put("Ceiling_Estimate", est);
                //unit.put("Ceiling_Estimate", "feet");
            //else {
                field.put("Ceiling", est);
                unit.put("Ceiling", "feet");
            //
            remark = m.replaceFirst(" ");
        }

        // 	Variable Sky conditions
        //	if( s#(FEW|SCT|BKN|OVC)(\d3)? V (FEW|SCT|BKN|OVC)\s+## )
        //		$VrbSkyBelow = $1 ;
        //		$VrbSkyLayerHgt = $2 * 100 if( defined( $2 ) ) ;
        //		$VrbSkyAbove = $3 ;
        m = MP.variableSky.matcher(remark);
        if (m.find()) {
            field.put("Variable_Sky_Below", m.group(1));
            unit.put("Variable_Sky_Below", "feet");
            if (m.group(2) != null) {
                field.put("Variable_Sky_Height", Integer.toString(Integer.parseInt(m.group(2)) * 100));
                unit.put("Variable_Sky_Height", "feet");
            }
            field.put("Variable_Sky_Above", m.group(3));
            unit.put("Variable_Sky_Above", "feet");
            remark = m.replaceFirst(" ");
        }

/* let these end up in extra_remarks

// 	Significant Cloud Types
//	if( s#(CB|CBMAM|TCU|ACC|SCSL|ACSL|ROTOR CLD|ROPE|ROPE CLD)\s+## ) 
//		$Sign_cloud = padstr( $1, 10 ) ;
//		$Sign_dist = padstr( $1, 10 ) 
//			if( s#^(VCNTY STN|VCY STN|VC STN|VCY|VC|DSNT|OMT)\s+## ) ;
//		$Sign_dir = padstr( "$1$2$3", 10 ) 
//			if( s#^(NE|NW|SE|SW|N|S|E|W)(\-| MOV )?(NE|NW|SE|SW|N|S|E|W)?/?\s+## ) ;
        m = MP.significantCloud.matcher(remark);
        if (m.find()) {
            field.put("Significant_Cloud", m.group(1));
            unit.put("Significant_Cloud", "");
            remark = m.replaceFirst(" ");
            m = MP.significantCloud1.matcher(remark);
            if (m.find()) {
                field.put("Significant_Cloud_Vicinity", m.group(1));
                unit.put("Significant_Cloud_Vicinity", "" );
                remark = m.replaceFirst(" ");
                m = MP.significantCloud2.matcher(remark);
                if (m.find()) {
                    field.put("Significant_Cloud_Direction", m.group(1));
                    unit.put("Significant_Cloud_Direction", "");
                    remark = m.replaceFirst(" ");
                }
            }
        }
*/

/* let these end up in extra_remarks
// 	Obscuring Phenomena Aloft
//	if( s#(VC|PR)?(MI|BC|DR|BL|SH|TS|FZ)?(DZ|RA|SN|SG|IC|PE|PL|GR|GS|UP)?(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS|DS)? (FEW|SCT|BKN|OVC)(\d3)\s+## ) 
//		$ObscurAloft = padstr( "$1$2$3$4$5", 8 ) ;
//		$ObscurAloftSkyCond = $6 ;
//		$ObscurAloftHgt = $7 * 100 ;
//
        m = MP.obscuringPhen.matcher(remark);
        if (m.find()) {
            String tmp = "";
            if (m.group(1) != null) {
                tmp = m.group(1);
            }
            if (m.group(2) != null) {
                tmp = m.group(2);
            }
            if (m.group(3) != null) {
                tmp = m.group(3);
            }
            if (m.group(4) != null) {
                tmp = m.group(4);
            }
            if (m.group(5) != null) {
                tmp = m.group(5);
            }
            if ( ! tmp.equals("")) {
                field.put("Surface_Phenomena", tmp );
                unit.put("Surface_Phenomena", "" );
            }
            field.put("Obscuring_Phenomena_Sky", m.group(6));
            unit.put("Obscuring_Phenomena_Sky", "");
            if (m.group(7) != null) {
                field.put("Obscuring_Phenomena_Sky_Height", Integer.toString(Integer.parseInt(m.group(7)) * 100));
                unit.put("Obscuring_Phenomena_Sky_Height", "feet");
            }
            remark = m.replaceFirst(" ");
        }
*/

        // 	Air craft mishap  ACFTMSHP
        //	$ACFTMSHP = 1 if( s#\(?ACFT\s?MSHP\)?\s+## ) ;
        m = MP.ACFT.matcher(remark);
        if (m.find()) {
            field.put("Air_craft_mishap", "yes");
            unit.put("Air_craft_mishap", "");
            remark = m.replaceFirst(" ");
        }

        // 	No changes in weather conditions until next report  NOSPECI
        //	$NOSPECI = 1 if( s#NOSPECI\s+## ) ;
        m = MP.NOSPECI.matcher(remark);
        if (m.find()) {
            field.put("Changes_in_weather", "No");
            unit.put("Changes_in_weather", "");
            remark = m.replaceFirst(" ");
        }

        // 	This is first report of the day  FIRST
        //	$FIRST = 1 if( s#FIRST\s+## ) ;
        m = MP.FIRST.matcher(remark);
        if (m.find()) {
            field.put("First_Report_Today", "yes");
            unit.put("First_Report_Today", "");
            remark = m.replaceFirst(" ");
        }

        // 	This is last report in observation coverage  LAST
        //	$LAST = 1 if( s#LAST\s+## ) ;
        m = MP.LAST.matcher(remark);
        if (m.find()) {
            field.put("Last_Report_Today", "yes");
            unit.put("Last_Report_Today", "");
            remark = m.replaceFirst(" ");
        }

        // 	Cloud Types
        //	if( s# 8/(\d|/)(\d|/)(\d|/)\s+# # )
        //		$Cloud_low = $1 ;
        //		$Cloud_medium = $2 ;
        //		$Cloud_high = $3 ;
        m = MP.cloud_height.matcher(remark);
        if (m.find()) {
            if( ! m.group(1).equals( "/")) {
                field.put("Cloud_Low", m.group(1));
                unit.put("Cloud_Low", "");
            }
            if( ! m.group(2).equals( "/")) {
                field.put("Cloud_Medium", m.group(2));
                unit.put("Cloud_Medium", "");
            }
            if( ! m.group(3).equals( "/")) {
                field.put("Cloud_High", m.group(3));
                unit.put("Cloud_High", "");
            }
            remark = m.replaceFirst(" ");
        }
        //
        // 	Snow Increasing Rapidly   SNINCR
        //	if( s#SNINCR (\d1,3)/(\d1,3)\s+## )
        //		$SNINCR = $1 ;
        //		$SNINCR_TotalDepth = $2 ;
        m = MP.SNINCR.matcher(remark);
        if (m.find()) {
            field.put("Snow_Increasing_Rapidly", m.group(1));
            unit.put("Snow_Increasing_Rapidly", "inches");
            field.put("Snow_Increasing_Depth", m.group(2));
            unit.put("Snow_Increasing_Depth", "inches");
            remark = m.replaceFirst(" ");
        }
        //
        // 	Snow depth on ground
        //	if( s#4/(\d1,3)\s+# # )
        //		$SN_depth = $1 ;
        m = MP.snowDepth.matcher(remark);
        if (m.find()) {
            field.put("Snow_Depth", m.group(1));
            unit.put("Snow_Depth", "inches");
            remark = m.replaceFirst(" ");
        }
        //
        //	 Water equivalent of snow on ground
        //	$SN_waterequiv = $1 / 10 if( s# 933(\d3)\s+# # ) ;
        m = MP.waterEquiv.matcher(remark);
        if (m.find()) {
            field.put("Water_Equivalent_of_Snow", Double.toString(Float.parseFloat(m.group(1)) * 0.1 ));
            unit.put("Water_Equivalent_of_Snow", "");
            remark = m.replaceFirst(" ");
        }

        // 	Duration of sunshine
        //	if( s# 98(\d1,3|///)\s+# # )
        //		if( $1 eq "///" )
        //			$SunSensorOut = 1 ;
        //		 else
        //			$SunShineDur = $1 ;
        m = MP.sunShine.matcher(remark);
        if (m.find()) {
            if( m.group(1).equals("///") ) {
                field.put("Sun_Sensor_working", "No");
                unit.put("Sun_Sensor_working", "");
            } else {
                field.put("Sun_Sensor_Duration", m.group(1));
                unit.put("Sun_Sensor_Duration", "");
            }
            remark = m.replaceFirst(" ");
        }

        // 	Precipitation amount
        //	if( s# 6(\d4|////)\s+# # )
        //		$PRECIP_amt = $1 / 100 if( $1 ne "////" ) ;
        m = MP.precipitation.matcher(remark);
        if (m.find()) {
            if( ! m.group(1).equals("////") ) {
                field.put("Precipitation_amount", Double.toString(Float.parseFloat(m.group(1)) * 0.01 ));
                unit.put("Precipitation_amount", "inches");
            }
            remark = m.replaceFirst(" ");
        }
        //
        // 	24 Hour Precipitation amount
        //	if( s# 7(\d4|////)\s+# # )
        //		$PRECIP_24_amt = $1 / 100 if( $1 ne "////" ) ;
        m = MP.precipitation24.matcher(remark);
        if (m.find()) {
            if( ! m.group(1).equals("////") ) {
                field.put("Precipitation_amount_24Hours", Double.toString(Float.parseFloat(m.group(1)) * 0.01 ));
                unit.put("Precipitation_amount_24Hours", "inches");
            }
            remark = m.replaceFirst(" ");
        }
        //
        // 	Maximum Temperature
        //	if( s# 1(0|1|/)(\d3|///)\s+# # )
        //		$Tmax = $2 / 10 if( $2 ne "///" ) ;
        //		$Tmax *= -1.0 if( $1 == 1 ) ;
        //
        m = MP.maxTemperature.matcher(remark);
        if (m.find()) {
            if( ! m.group(2).equals("///") ) {
                double maxtemp = Float.parseFloat(m.group(2));
                if( m.group(1).equals("1") ) {
                    maxtemp *= -0.1;
                } else if( m.group(1).equals("0") ) {
                    maxtemp *= 0.1;
                }
                field.put("Max_Temperature", Double.toString( maxtemp ) );
                unit.put("Max_Temperature", "Celsius");
            }
            remark = m.replaceFirst(" ");
        }

        // 	Minimum Temperature
        //	if( s# 2(0|1|/)(\d3|///)\s+# # )
        //		$Tmin = $2 / 10 if( $2 ne "///" ) ;
        //		$Tmin *= -1.0 if( $1 == 1 ) ;
        m = MP.minTemperature.matcher(remark);
        if (m.find()) {
            if( ! m.group(2).equals("///") ) {
                double mintemp = Float.parseFloat(m.group(2));
                if( m.group(1).equals("1") ) {
                    mintemp *= -0.1;
                } else if( m.group(1).equals("0") ) {
                    mintemp *= 0.1;
                }
                field.put("Min_Temperature", Double.toString( mintemp ) );
                unit.put("Min_Temperature", "Celsius");
            }
            remark = m.replaceFirst(" ");
        }
        //
        // 	24-Hour Maximum and Minimum Temperature
        //	if( s# 4(0|1|/)(\d3|///)(0|1|/)(\d3|///)\s+# # )
        //		$Tmax24 = $2 / 10 if( $2 ne "///" ) ;
        //		$Tmax24 *= -1.0 if( $1 == 1 ) ;
        //		$Tmin24 = $4 / 10 if( $4 ne "///" ) ;
        //		$Tmin24 *= -1.0 if( $3 == 1 ) ;
        m = MP.maxMinTemp24.matcher(remark);
        if (m.find()) {
            if( ! m.group(2).equals("///") ) {
                double maxtemp = Float.parseFloat(m.group(2));
                if( m.group(1).equals("1") ) {
                    maxtemp *= -0.1;
                } else if( m.group(1).equals("0") ) {
                    maxtemp *= 0.1;
                }
                field.put("Max_Temperature_24Hour", Double.toString( maxtemp ) );
                unit.put("Max_Temperature_24Hour", "Celsius");
            }
            if( ! m.group(4).equals("///") ) {
                double mintemp = Float.parseFloat(m.group(4));
                if( m.group(3).equals("1") ) {
                    mintemp *= -0.1;
                } else if( m.group(3).equals("0") ) {
                    mintemp *= 0.1;
                }
                field.put("Min_Temperature_24Hour", Double.toString( mintemp ) );
                unit.put("Min_Temperature_24Hour", "Celsius");
            }
            remark = m.replaceFirst(" ");
        }
        //
        // 	Presure Tendency
        //	if( s# 5(0|1|2|3|4|5|6|7|8)(\d3/?|///)\s+# # )
        //		$char_Ptend = $1 ;
        //		$Ptend = $2 / 10 if( $2 ne "///" ) ;
        //
        m = MP.pressureTendency.matcher(remark);
        if (m.find()) {
            field.put("Presure_Tendency_char", m.group(1));
            unit.put("Presure_Tendency_char", "");
            if( ! m.group(2).equals("///") ) {
                field.put("Presure_Tendency", Double.toString(Float.parseFloat(m.group(2)) * 0.1 ));
                unit.put("Presure_Tendency", "hectopascals");
            }
            remark = m.replaceFirst(" ");
        }

        // 	Freezing Rain sensor not working  FZRANO
        //	$FZRANO = 1 if( s#FZRANO\s+## ) ;
        m = MP.FZRANO.matcher(remark);
        if (m.find()) {
            field.put("Freezing_Rain_sensor_working", "No");
            unit.put("Freezing_Rain_sensor_working", "");
            remark = m.replaceFirst(" ");
        }

        // 	Tipping bucket rain gauge is inoperative.
        //	$PNO = 1 if( s#PNO\s+## ) ;
        m = MP.PNO.matcher(remark);
        if (m.find()) {
            field.put("Tipping_bucket_rain_gauge_working", "No");
            unit.put("Tipping_bucket_rain_gauge_working", "");
            remark = m.replaceFirst(" ");
        }

        // 	Maintenance is needed on system Indicator
        //	$maintIndicator = 1 if( s#\$\s+## ) ;
        m = MP.maintenace.matcher(remark);
        if (m.find()) {
            field.put("Maintenance_needed", "yes");
            unit.put("Maintenance_needed", "");
            remark = m.replaceFirst(" ");
        }

        /* let this go to the extra fields field cuz it's too general
// 	Get Recent weather conditions with Beginning and Ending times, moved 
//	because the RE are too general and they match wrongly
// 	code table 4678
	for(int i = 0; i < 3; i++ ) {
            String RWX = "Recent_Weather_"+ Integer.toString( i +1 );
//	    if( s#(\+|-|VC|PR)?(MI|BC|DR|BL|SH|TS|FZ)?(DZ|RA|SN|SG|IC|PE|PL|GR|GS|UP)?(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS|DS)?B(\d2,4)E(\d2,4)\s+## ) 
            m = MP.recentWeather.matcher(remark);
            if (m.find()) {
//		$Recent_WX[ $i ] = padstr( "$1$2$3$4$5", 8 ) ;
                String tmp = "";
                if (m.group(1) != null) {
                    tmp = m.group(1);
                }
                if (m.group(2) != null) {
                    tmp = tmp + m.group(2);
                }
                if (m.group(3) != null) {
                    tmp = tmp + m.group(3);
                }
                if (m.group(4) != null) {
                    tmp = tmp + m.group(4);
                }
                if (m.group(5) != null) {
                    tmp = tmp + m.group(5);
                }
                if ( ! tmp.equals("")) {
                    field.put( RWX, tmp );
                    unit.put( RWX, "" );
                } else {
                    break;
                }
//		if( length( $6 ) == 5 ) 
//			$Recent_WX_Bhh[ $i ] = substr( $6, 1, 2 ) * 1 ;
//			$Recent_WX_Bmm[ $i ] = substr( $6, 3, 2 ) * 1 ;
//		elsif( length( $6 ) == 3 ) 
//			$Recent_WX_Bmm[ $i ] = substr( $6, 1, 2 ) * 1 ;
                field.put( RWX +"_Begin_Time", m.group(6) );
                unit.put( RWX +"_Begin_Time", "" );
//		if( length( $7 ) == 5 ) 
//			$Recent_WX_Ehh[ $i ] = substr( $7, 1, 2 ) * 1 ;
//			$Recent_WX_Emm[ $i ] = substr( $7, 3, 2 ) * 1 ;
//	   	elsif( length( $7 ) == 3 ) 
//			$Recent_WX_Emm[ $i ] = substr( $7, 1, 2 ) * 1 ;
                field.put( RWX +"_End_Time", m.group(7) );
                unit.put( RWX +"_End_Time", "" );
                remark = m.replaceFirst(" ");
//	    elsif( s#(\+|-|VC|PR)?(MI|BC|DR|BL|SH|TS|FZ)?(DZ|RA|SN|SG|IC|PE|PL|GR|GS|UP)?(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS|DS)?(B|E)(\d2,4)\s+## ) 
            } else {
                m = MP.recentWeather1.matcher(remark);
                if (m.find()) {
//		$Recent_WX[ $i ] = padstr( "$1$2$3$4$5", 8 ) ;
                    String tmp = "";
                    if (m.group(1) != null) {
                        tmp = m.group(1);
                    }
                    if (m.group(2) != null) {
                        tmp = tmp + m.group(2);
                    }
                    if (m.group(3) != null) {
                        tmp = tmp + m.group(3);
                    }
                    if (m.group(4) != null) {
                        tmp = tmp + m.group(4);
                    }
                    if (m.group(5) != null) {
                        tmp = tmp + m.group(5);
                    }
                    if ( ! tmp.equals("")) {
                        field.put( RWX, tmp );
                        unit.put( RWX, "" );
                    } else {
                        break;
                    }
//		if( $6 eq "B" && ( length( $7 ) == 4 )) 
//			$Recent_WX_Bhh[ $i ] = substr( $7, 0, 2 ) * 1 ;
//			$Recent_WX_Bmm[ $i ] = substr( $7, 2, 2 ) * 1 ;
//		 elsif( $6 eq "B" && ( length( $7 ) == 2 )) 
//			$Recent_WX_Bmm[ $i ] = substr( $7, 0, 2 ) * 1 ;
                    if( m.group(6).equals("B" ) ) {
                        field.put( RWX +"_Begin_Time", m.group(7) );
                        unit.put( RWX +"_Begin_Time", "" );
//		 elsif( $6 eq "E" && ( length( $7 ) == 4 )) 
//			$Recent_WX_Ehh[ $i ] = substr( $7, 0, 2 ) * 1 ;
//			$Recent_WX_Emm[ $i ] = substr( $7, 2, 2 ) * 1 ;
//		 elsif( $6 eq "E" && ( length( $7 ) == 2 )) 
//			$Recent_WX_Emm[ $i ] = substr( $7, 0, 2 ) * 1 ;
                     } else {
                        field.put( RWX +"_End_Time", m.group(7) );
                        unit.put( RWX +"_End_Time", "" );
		             }
                     remark = m.replaceFirst(" ");
	        } else {
//		last ;
                    break;
	        }
            }
        } // end for recent weather
*/
        // 	Extra remarks includes Volcanic eruptions
        m = MP.spaces.matcher(remark);
        if (m.find()) {
            remark = m.replaceFirst("");
        }
        if( remark.length() != 0) {
            field.put("Extra_fields", remark );
            unit.put("Extra_fields", "");
        }

        return true;
    } // end parseReport

// convert cloud height to  meters
    private String cloud_hgt2_meters(String height) {

        if (height.equals("999")) {
            return "30000";
        } else {
//		$meters = 30 * $height ;
            return Integer.toString(30 * Integer.parseInt(height));
        }
    } // end cloud_hgt2_meters

    /**
     * Used to return fields in Metar report.
     * @return LinkedHashMap
     **/
     public LinkedHashMap getFields() {
        return field;
     }

    /**
     * Used to return units of the fields in Metar report.
     * @return LinkedHashMap
     **/
     public HashMap getUnits() {
        return unit;
     }

    public static void main(String args[]) throws IOException {

        String report = null;
        //report = "KD07 150256Z AUTO 28005KT BR M08/M11 A3005 RMK AO2 SLP223 T10781111 21205 40051//// 50006 PWINO FZRANO";
        //report = "METAR K1V4 251254Z AUTO 01/01 A3002 RMK AO2 SLP172 P0001 T00110006 PWINO FZRANO TSNO ";
        //report = "KDEN 201453Z 35007KT 1/4SM R35L/5000V6000FT -DZ BR -SG -FZDZ OVC003 05/04 A2996 RMK 70155 53777 61111 8/53/";
        //report = "ROBB 201453Z 35007KT 1/4SM RMK AO1 21200 4/12 T00110006 SLPNO SLP067 P0000";
        //report = "ROBB 201453Z NVRB05G15KT 090V180 1/4SM AUTO RMK PWINO TSNO TORNADO B10 DSNT NE";
        //report = "ROBB 201453Z NVRB05G15KT R26/0450V0650D RMK PWINO TSNO TORNADO B10 DSNT NE";
        //report = "ROBB 201453Z SKC VV312 RMK TORNADO B10 DSNT NE";
        //report = "KDEN 201453Z 35007KT 1/4SM R35L/5000V6000FT -DZ BR -SG -FZDZ OVC003 05/04 A2996 RMK CIG 003V006";
        //report = "KDEN 201553Z 01006KT 1/4SM R35L/4500VP6000FT  -DZ BR OVC003 05/04 A2996 RMK AO2 SFC VIS 3/4 CIG 003V006 SLP124 P0000 T00500044";
        //report = "SOCA 202000Z 10003KT 9999 VCSH FEW010CB SCT015TCU 27/26 Q1010 NOSIG TEMPO 4000 CB & TCU AU S ET W";
        //report = "SOCA 202000Z 10003KT RMK LTG DSNT SE VIS 6 5/10 RY11 WSHFT 1853 FROPA SLP575 TEMPO 4000 CB & TCU AU S ET W";
        report = "SOCA 202000Z 10003KT RMK 98015 933002" +
                " CIGNO CIG 125 OVC V BKN -FZRA $";
        // Function References
        MetarParseReport func = new MetarParseReport();

        if( args.length == 1 ) { // read external file of Metar reports
            System.out.println( args[ 0 ] +" "+ args.length );
            // read reports from file to parse
            try (InputStream ios = new FileInputStream( args[ 0 ] )) {
              BufferedReader dataIS = new BufferedReader(new InputStreamReader(ios));
              //System.out.println("");
              while (true) {
                report = dataIS.readLine();
                if (report == null) {
                  break;
                }
                System.out.println("\n" + report);
                if (!func.parseReport(report)) {
                  System.out.println("badly formed report, can't parse");
                  continue; // bad report, can't parse
                }

                LinkedHashMap field = func.getFields();
                HashMap unit = func.getUnits();
                String key;

                System.out.println("");
                for (Iterator it = field.keySet().iterator(); it.hasNext(); ) {
                  key = (String) it.next();
                  //System.out.println(key + "\t\t" + (String) field.get(key));
                  System.out.println("\t");
                }
                System.out.println("");
              }

              //System.out.println("");
              return;
            }
        }


        // else
        System.out.println( report );
        if( ! func.parseReport(report) ) {
            System.out.println("badly formed report, can't parse");
            System.exit(1);
        }
        LinkedHashMap field = func.getFields();
        HashMap unit = func.getUnits();
        String key;

        System.out.println("");
        for( Iterator it = field.keySet().iterator(); it.hasNext(); ) {
            key = (String) it.next();
            //System.out.println(key + "\t\t" + (String) field.get(key));
            System.out.println("\t" );
        }
        System.out.println("");

    }

} // end MetarParseReport




© 2015 - 2024 Weber Informatics LLC | Privacy Policy