<?PHP
#
#   FILE:  LatLongPoints.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2013 Edward Almasy and Internet Scout
#   http://scout.wisc.edu/cwis
#

/**
* Plugin for accepting and displaying values in latitude/logitude format
* in Point metadata fields.
*/
class LatLongPoints extends Plugin {

    /**
    * Called by the PluginManager to register the plugin.
    */
    function Register()
    {
        $this->Name = "Latitude/Longitude Points";
        $this->Version = "1.1.0";
        $this->Description = "The Latitude/Longitude Points plugin makes any"
                ." Point metadata fields accept input and display in common"
                ." latitude/longitude format (e.g. <i>43&deg; 21' 10&quot; N</i>).";
        $this->Author = "Internet Scout";
        $this->Url = "http://scout.wisc.edu/cwis/";
        $this->Email = "scout@scout.wisc.edu";
        $this->Requires = array("CWISCore" => "2.1.0");
        $this->CfgSetup["FieldOne"] = array(
                "Type" => "MetadataField",
                "Label" => "Lat/Long Field",
                "Help" => "Point field to accept and display"
                        ." latitude/longitude coordinates.",
                "FieldTypes" => MetadataSchema::MDFTYPE_POINT,
                );
        $this->CfgSetup["FieldTwo"] = $this->CfgSetup["FieldOne"];
        $this->CfgSetup["FieldThree"] = $this->CfgSetup["FieldOne"];
    }

    /**
    * Called by the PluginManager to retrieve events to be hooked for the plugin.
    * @return Associative array with event names for the index and method names
    *       for the values.
    */
    function HookEvents()
    {
        return array(
                "EVENT_FIELD_DISPLAY_FILTER" => "LatLongDecimalToDegrees",
                "EVENT_PRE_FIELD_EDIT_FILTER" => "LatLongDecimalToDegrees",
                "EVENT_POST_FIELD_EDIT_FILTER" => "LatLongDegreesToDecimal",
                );
    }

    /**
    * Hooked to convert stored Point field values to their equivalent in the
    * traditional latitude/longitude format before they're edited or displayed,
    * if the field is one of those configured.
    * This is hooked to EVENTTYPE_CHAIN events, so it must return an array
    * containing the incoming parameters, for use by the next hooked function.
    * @param Field Metadata field with value to be potentially modified.
    * @param Resource Resource containing value to be potentially modified.
    * @param Value Value to be potentially modified.
    * @return Incoming parameters with potentially modified Value.
    */
    function LatLongDecimalToDegrees($Field, $Resource, $Value)
    {
        global $SysConfig;

        $CharacterSet = $SysConfig->DefaultCharacterSet();

        $ReturnValue = array(
                "Field" => $Field,
                "Resource" => $Resource,
                "Value" => $Value);
        $LatLongFields = array(
                $this->ConfigSetting("FieldOne"),
                $this->ConfigSetting("FieldTwo"),
                $this->ConfigSetting("FieldThree"),
                );
        if (in_array($Field->Id(), $LatLongFields))
        {
            foreach (array("X", "Y") as $Axis)
            {
                if (floatval($Value[$Axis]))
                {
                    $Coord = $Value[$Axis];
                    $Degrees = floor(abs($Coord));
                    $Minutes = floor((abs($Coord) - $Degrees) * 60);
                    $Seconds = round((abs($Coord)
                            - $Degrees - ($Minutes / 60)) * 3600);
                    $DegreeSymbol = html_entity_decode(
                        "&deg; ",
                        ENT_COMPAT,
                        $CharacterSet);

                    $ReturnValue["Value"][$Axis] =
                            $Degrees.$DegreeSymbol
                            .$Minutes."' ".$Seconds."\" "
                            .(($Coord >= 0) ? (($Axis == "X") ? "N" : "E")
                            : (($Axis == "X") ? "S" : "W"));
                }
            }
        }
        return $ReturnValue;
    }

    /**
    * Hooked to convert latitude/longitude values to their decimal equivalent
    * for storage in a Point field, if the metadata field is one of those
    * selected in the plugin configuration.
    * This is hooked to EVENTTYPE_CHAIN events, so it must return an array
    * containing the incoming parameters, for use by the next hooked function.
    * @param Field Metadata field with value to be potentially modified.
    * @param Resource Resource containing value to be potentially modified.
    * @param Value Value to be potentially modified.
    * @return Incoming parameters with potentially modified Value.
    */
    function LatLongDegreesToDecimal($Field, $Resource, $Value)
    {
        $ReturnValue = array(
                "Field" => $Field,
                "Resource" => $Resource,
                "Value" => $Value);
        $LatLongFields = array(
                $this->ConfigSetting("FieldOne"),
                $this->ConfigSetting("FieldTwo"),
                $this->ConfigSetting("FieldThree"),
                );
        if (in_array($Field->Id(), $LatLongFields))
        {
            # for each of the coordinate
            foreach (array("X", "Y") as $Axis)
            {
                # remove all extraneous characters
                $Value = strtoupper(trim($ReturnValue["Value"][$Axis]));
                $Value = preg_replace("/[^0-9SNEW.\s-]/", " ", $Value);

                # split up into individual components
                $Pieces = preg_split("/\s+/", $Value);

                # if components were found
                if (count($Pieces) && floatval($Pieces[0]))
                {
                    # calculate fractional value from remaining components
                    $NewValue = 0;
                    $Divisor = 1;
                    foreach ($Pieces as $Piece)
                    {
                        $NewValue += $Piece / $Divisor;
                        $Divisor *= 60;
                    }

                    # flip sign if direction indicates
                    if ((substr($Piece, -1) == "W") || (substr($Piece, -1) == "S"))
                    {
                        $NewValue = 0 - $NewValue;
                    }

                    # save new decimal value
                    $ReturnValue["Value"][$Axis] = $NewValue;
                }
            }
        }
        return $ReturnValue;
    }
}

?>
