text:
// ----------------------------------------------------------------------------
// Copyright 2007-2015, GeoTelematic Solutions, Inc.
// All rights reserved
// ----------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ----------------------------------------------------------------------------
// Change History:
//  2008/12/01  Martin D. Flynn
//     -Initial release
//  2009/09/23  Martin D. Flynn
//     -Added "&oe=utf8" to reverse-geocode request url
//  2009/11/01  Martin D. Flynn
//     -Added check for error code "620" (ie. limit exceeded)
//  2009/12/16
//     -Added "reverseGeocodeURL", "geocodeURL" properties.
//     -Added support for client-id (ie. "&client=gme-...")
//     -Added support for Geocoding
// ----------------------------------------------------------------------------
package org.opengts.geocoder.google;

import java.util.*;
import java.io.*;

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;

import org.opengts.util.*;

import org.opengts.db.*;
import org.opengts.geocoder.*;

public class GoogleGeocodeV2
    extends ReverseGeocodeProviderAdapter
    implements ReverseGeocodeProvider, GeocodeProvider
{

    // ------------------------------------------------------------------------
    // References:
    //   - http://googlegeodevelopers.blogspot.com/2008/10/geocoding-in-reverse.html
    //   - http://blog.charlvn.za.net/2008/10/google-reverse-geocoding.html
    //   - http://code.google.com/apis/maps/documentation/services.html#Geocoding_Direct
    //   - http://code.google.com/apis/maps/documentation/geocoding/index.html
    //
    // Nearest Address: V2 API
    //   - http://maps.google.com/maps/geo?output=xml&ll=40.479581,-117.773438&key=(GoogleKey)
    //     Returns an XML file:
    //     <kml>
    //      <Response>
    //        <name>40.479581,-117.773438</name>
    //        <Status>
    //          200
    //          <request>geocode</request>
    //        </Status>
    //        <Placemark id="p1">
    //          <address>Apache, NV 89418, USA</address>
    //          <AddressDetails Accuracy="5">
    //            <Country>
    //              <CountryNameCode>US</CountryNameCode>
    //              <CountryName>USA</CountryName>
    //              <AdministrativeArea>
    //                <AdministrativeAreaName>NV</AdministrativeAreaName>
    //                <Locality>
    //                  <LocalityName>Apache</LocalityName>
    //                  <PostalCode>
    //                    <PostalCodeNumber>89418</PostalCodeNumber>
    //                  </PostalCode>
    //                </Locality>
    //              </AdministrativeArea>
    //            </Country>
    //          </AddressDetails>
    //          <Point>
    //            <coordinates>-118.0186087,40.6177933,0</coordinates>
    //          </Point>
    //        </Placemark>
    //      </Response>
    //     </kml>
    //
    // Exceeding reverse-geocode limit: V2 API
    //     <?xml version="1.0" encoding="UTF-8" ?>
    //     <kml xmlns="http://earth.google.com/kml/2.0">
    //       <Response>
    //         <name>40.479581,-117.773438</name>
    //         <Status>
    //           620
    //           <request>geocode</request>
    //         </Status>
    //       </Response>
    //     </kml>
    // - Have also seen code "602" ("G_GEO_UNKNOWN_ADDRESS")
    //
    // ------------------------------------------------------------------------

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------
    // V2 API

    /* V2 Tags */
    protected static final String TAG_kml                       = "kml";           // main tag
    protected static final String TAG_Response                  = "Response";
    protected static final String TAG_name                      = "name";
    protected static final String TAG_Status                    = "Status";
    protected static final String TAG_code                      = "code";
    protected static final String TAG_request                   = "request";
    protected static final String TAG_Placemark                 = "Placemark";
    protected static final String TAG_address                   = "address";
    protected static final String TAG_AddressDetails            = "AddressDetails";
    protected static final String TAG_Country                   = "Country";
    protected static final String TAG_CountryNameCode           = "CountryNameCode";
    protected static final String TAG_CountryName               = "CountryName";
    protected static final String TAG_AdministrativeArea        = "AdministrativeArea";
    protected static final String TAG_AdministrativeAreaName    = "AdministrativeAreaName";
    protected static final String TAG_Locality                  = "Locality";
    protected static final String TAG_LocalityName              = "LocalityName";
    protected static final String TAG_PostalCode                = "PostalCode";
    protected static final String TAG_PostalCodeNumber          = "PostalCodeNumber";
    protected static final String TAG_Point                     = "Point";
    protected static final String TAG_coordinates               = "coordinates";

    /* V2 Attributes */
    protected static final String ATTR_id                       = "id";
    protected static final String ATTR_Accuracy                 = "Accuracy";

    /* V2 URLs */
    protected static final String URL_ReverseGeocode_           = "http://maps.google.com/maps/geo?";
    protected static final String URL_Geocode_                  = "http://maps.google.com/maps/geo?";

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    protected static final String PROP_reverseGeocodeURL        = "reverseGeocodeURL";
    protected static final String PROP_geocodeURL               = "geocodeURL";
    protected static final String PROP_sensor                   = "sensor";
    protected static final String PROP_channel                  = "channel";
    protected static final String PROP_countryCodeBias          = "countryCodeBias"; // http://en.wikipedia.org/wiki/CcTLD
    protected static final String PROP_signatureKey             = "signatureKey";

    // ------------------------------------------------------------------------

    protected static final int    TIMEOUT_ReverseGeocode        = 2500; // milliseconds
    protected static final int    TIMEOUT_Geocode               = 5000; // milliseconds

    protected static final String DEFAULT_COUNTRY               = "US"; // http://en.wikipedia.org/wiki/CcTLD

    protected static final String CLIENT_ID_PREFIX              = "gme-";

    protected static final boolean FAILOVER_DEBUG               = false;

    // ------------------------------------------------------------------------

    // address has to be within this distance to qualify (cannot be greater than 5.0 kilometers)
    protected static final double MAX_ADDRESS_DISTANCE_KM       = 1.1;
   // protected static final double MAX_ADDRESS_DISTANCE_KM = 4.5;

    // ------------------------------------------------------------------------

    protected static final String ENCODING_UTF8                 = StringTools.CharEncoding_UTF_8;

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    private boolean      signature_init = false;
    private GoogleSig    signature      = null;

    public GoogleGeocodeV2(String name, String key, RTProperties rtProps)
    {
        super(name, key, rtProps);
    }

    // ------------------------------------------------------------------------

    public boolean isFastOperation()
    {
        // this is a slow operation
        return super.isFastOperation();
    }

    // ------------------------------------------------------------------------

    public GoogleSig getSignature()
    {
        if (!this.signature_init) {
            this.signature_init = true;
            String key = this.getAuthorization();
            if (!StringTools.isBlank(key) && key.startsWith(CLIENT_ID_PREFIX)) {
                String sigKey = this.getProperties().getString(PROP_signatureKey,"");
                if (!StringTools.isBlank(sigKey)) {
                    Print.logWarn("Setting SignatureKey: " + sigKey);
                    this.signature = new GoogleSig(sigKey);
                } else {
                    Print.logWarn("No signatureKey ...");
                }
            }
        }
        return this.signature;
    }

    // ------------------------------------------------------------------------

    /**
    *** Returns the Geocode timeout
    **/
    protected int getGeocodeTimeout()
    {
        return TIMEOUT_Geocode;
    }

    /**
    *** Returns the ReverseGeocode timeout
    **/
    protected int getReverseGeocodeTimeout()
    {
        return TIMEOUT_ReverseGeocode;
    }

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    /* return reverse-geocode */
    public ReverseGeocode getReverseGeocode(GeoPoint gp, String localeStr, boolean cache)
    {
        ReverseGeocode rg = this.getAddressReverseGeocode(gp, localeStr, cache);
        return rg;
    }

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    /* nearest address URI */
    protected String getAddressReverseGeocodeURI()
    {
        return URL_ReverseGeocode_;
    }

    /* encode GeoPoint into nearest address URI */
    protected String getAddressReverseGeocodeURL(GeoPoint gp, String localStr)
    {
        StringBuffer sb = new StringBuffer();
        GoogleSig sig = this.getSignature();

        /* predefined URL */
        String rgURL = this.getProperties().getString(PROP_reverseGeocodeURL,null);
        if (!StringTools.isBlank(rgURL)) {
            sb.append(rgURL);
            sb.append("&ll=").append(gp.getLatitudeString(GeoPoint.SFORMAT_DEC_5,null)).append(",").append(gp.getLongitudeString(GeoPoint.SFORMAT_DEC_5,null));
            String defURL = sb.toString();
            if (sig == null) {
                return defURL;
            } else {
                String urlStr = sig.signURL(defURL);
                return (urlStr != null)? urlStr : defURL;
            }
        }

        /* assemble URL */
        sb.append(this.getAddressReverseGeocodeURI());
        sb.append("output=xml");
        sb.append("&oe=utf8");

        /* latitude/longitude */
        sb.append("&ll=").append(gp.getLatitudeString(GeoPoint.SFORMAT_DEC_5,null)).append(",").append(gp.getLongitudeString(GeoPoint.SFORMAT_DEC_5,null));

        /* sensor */
        String sensor = this.getProperties().getString(PROP_sensor, null);
        if (!StringTools.isBlank(sensor)) {
            sb.append("&sensor=").append(sensor);
        }

        /* key */
        String channel = this.getProperties().getString(PROP_channel, null);
        String auth    = this.getAuthorization();
        if (StringTools.isBlank(auth) || auth.startsWith("*")) {
            // invalid key
        } else
        if (auth.startsWith(CLIENT_ID_PREFIX)) {
            sb.append("&client=").append(auth);
            if (StringTools.isBlank(channel)) {
                channel = DBConfig.getServiceAccountID(null);
            }
        } else {
            sb.append("&key=").append(auth);
        }

        /* channel */
        if (!StringTools.isBlank(channel)) {
            sb.append("&channel=").append(channel);
        }

        /* localization ("&hl=") */
        if (!StringTools.isBlank(localStr)) {
            sb.append("&hl=").append(localStr);
        }

        /* return url */
        String defURL = sb.toString();
        if (sig == null) {
            return defURL;
        } else {
            String urlStr = sig.signURL(defURL);
            return (urlStr != null)? urlStr : defURL;
        }

    }

    /* return reverse-geocode using nearest address */
    public ReverseGeocode getAddressReverseGeocode(GeoPoint gp, String localeStr, boolean cache)
    {

        /* check for failover mode */
        if (this.isReverseGeocodeFailoverMode()) {
            ReverseGeocodeProvider frgp = this.getFailoverReverseGeocodeProvider();
            return frgp.getReverseGeocode(gp, localeStr, cache);
        }

        /* URL */
        String url = this.getAddressReverseGeocodeURL(gp, localeStr);
        Print.logDebug("Google RG URL: " + url);
        //byte xmlBytes[] = HTMLTools.readPage(url);

        /* create XML document */
        Document xmlDoc = GetXMLDocument(url, this.getReverseGeocodeTimeout());
        if (xmlDoc == null) {
            return null;
        }

        /* vars */
        String errCode = null;
        String address = null;

        /* parse "xml" */
        Element kml = xmlDoc.getDocumentElement();
        if (kml.getTagName().equalsIgnoreCase(TAG_kml)) {
            NodeList ResponseList = XMLTools.getChildElements(kml,TAG_Response);
            for (int g = 0; (g < ResponseList.getLength()); g++) {
                Element response = (Element)ResponseList.item(g);
                NodeList responseNodes = response.getChildNodes();
                for (int n = 0; n < responseNodes.getLength(); n++) {
                    Node responseNode = responseNodes.item(n);
                    if (!(responseNode instanceof Element)) { continue; }
                    Element responseElem = (Element)responseNode;
                    String responseNodeName = responseElem.getNodeName();
                    if (responseNodeName.equalsIgnoreCase(TAG_name)) {
                        // <name>40.479581,-117.773438</name>
                    } else
                    if (responseNodeName.equalsIgnoreCase(TAG_Status)) {
                        // <Status> ... </Status>
                        NodeList statusNodes = responseElem.getChildNodes();
                        for (int st = 0; st < statusNodes.getLength(); st++) {
                            Node statusNode = statusNodes.item(st);
                            if (!(statusNode instanceof Element)) { continue; }
                            Element statusElem = (Element)statusNode;
                            String statusNodeName = statusElem.getNodeName();
                            if (statusNodeName.equalsIgnoreCase(TAG_code)) {
                                errCode = StringTools.trim(GoogleGeocodeV2.GetNodeText(statusElem));  // expect "200"
                                break; // we only care about the 'code'
                            }
                        }
                    } else
                    if (responseNodeName.equalsIgnoreCase(TAG_Placemark)) {
                        // <Placemark> ... </Placemark>
                        String id = responseElem.getAttribute(ATTR_id);
                        if ((id != null) && id.equals("p1")) {
                            NodeList placemarkNodes = responseElem.getChildNodes();
                            for (int pm = 0; pm < placemarkNodes.getLength(); pm++) {
                                Node placemarkNode = placemarkNodes.item(pm);
                                if (!(placemarkNode instanceof Element)) { continue; }
                                Element placemarkElem = (Element)placemarkNode;
                                String placemarkNodeName = placemarkElem.getNodeName();
                                if (placemarkNodeName.equalsIgnoreCase(TAG_address)) {
                                    address = GoogleGeocodeV2.GetNodeText(placemarkElem);
                                    break; // we only care about the 'address'
                                }
                            }
                        } else {
                            //Print.logInfo("Skipping Placemark ID = %s", id);
                        }
                    }
                }
            }
        }

        /* create address */
        if (FAILOVER_DEBUG) {
            errCode = "620";
        } else
        if (!StringTools.isBlank(address)) {
            // address found
            ReverseGeocode rg = new ReverseGeocode();
            rg.setFullAddress(address);
            return rg;
        }

        /* check for Google reverse-geocode limit exceeded */
        if ((errCode != null) && errCode.equals("620")) {
            Print.logError("!!!! Google Reverse-Geocode Limit Exceeded [Error 620] !!!!");
            if (this.hasFailoverReverseGeocodeProvider()) {
                this.startReverseGeocodeFailoverMode();
                ReverseGeocodeProvider frgp = this.getFailoverReverseGeocodeProvider();
                Print.logWarn("Failing over to '" + frgp.getName() + "'");
                return frgp.getReverseGeocode(gp, localeStr, cache);
            }
        }

        /* no reverse-geocode available */
        return null;

    }

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    /* nearest address URI */
    protected String getGeoPointGeocodeURI()
    {
        return URL_Geocode_;
    }

    /* encode GeoPoint into nearest address URI */
    protected String getGeoPointGeocodeURL(String address, String country)
    {
        StringBuffer sb = new StringBuffer();
        GoogleSig sig = this.getSignature();

        /* country */
        if (StringTools.isBlank(country)) {
            country = this.getProperties().getString(PROP_countryCodeBias, DEFAULT_COUNTRY);
        }

        /* predefined URL */
        String gcURL = this.getProperties().getString(PROP_geocodeURL,null);
        if (!StringTools.isBlank(gcURL)) {
            sb.append(gcURL);
            sb.append("&q=").append(URIArg.encodeArg(address));
            if (!StringTools.isBlank(country)) {
                // country code bias: http://en.wikipedia.org/wiki/CcTLD
                sb.append("&gl=").append(country);
            }
            return sb.toString();
        }

        /* assemble URL */
        sb.append(this.getGeoPointGeocodeURI());
        sb.append("output=xml");
        sb.append("&oe=utf8");

        /* address/country */
        sb.append("&q=").append(URIArg.encodeArg(address));
        if (!StringTools.isBlank(country)) {
            sb.append("&gl=").append(country);
        }

        /* sensor */
        String sensor= this.getProperties().getString(PROP_sensor, null);
        if (!StringTools.isBlank(sensor)) {
            sb.append("&sensor=").append(sensor);
        }

        /* channel */
        String channel = this.getProperties().getString(PROP_channel, null);
        if (!StringTools.isBlank(channel)) {
            sb.append("&channel=").append(channel);
        }

        /* key */
        String auth = this.getAuthorization();
        if (StringTools.isBlank(auth) || auth.startsWith("*")) {
            // invalid key
        } else
        if (auth.startsWith(CLIENT_ID_PREFIX)) {
            sb.append("&client=").append(auth);
        } else {
            sb.append("&key=").append(auth);
        }

        /* return url */
        String defURL = sb.toString();
        if (sig == null) {
            return defURL;
        } else {
            String urlStr = sig.signURL(defURL);
            return (urlStr != null)? urlStr : defURL;
        }

    }

    /* return geocode */
    // http://code.google.com/apis/maps/documentation/geocoding/index.html
    public GeoPoint getGeocode(String address, String country)
    {

        /* URL */
        String url = this.getGeoPointGeocodeURL(address, country);
        Print.logDebug("Google GC URL: " + url);

        /* create XML document */
        Document xmlDoc = GetXMLDocument(url, this.getGeocodeTimeout());
        if (xmlDoc == null) {
            return null;
        }

        /* vars */
        String errCode = null;
        GeoPoint geoPoint = null;

        /* parse "xml" */
        Element kml = xmlDoc.getDocumentElement();
        if (kml.getTagName().equalsIgnoreCase(TAG_kml)) {
            NodeList ResponseList = XMLTools.getChildElements(kml,TAG_Response);
            for (int g = 0; (g < ResponseList.getLength()); g++) {
                Element response = (Element)ResponseList.item(g);
                NodeList responseNodes = response.getChildNodes();
                for (int n = 0; n < responseNodes.getLength(); n++) {
                    Node responseNode = responseNodes.item(n);
                    if (!(responseNode instanceof Element)) { continue; }
                    Element responseElem = (Element)responseNode;
                    String responseNodeName = responseElem.getNodeName();
                    if (responseNodeName.equalsIgnoreCase(TAG_name)) {
                        // <name>1600 Amphitheatre Parkway, Mountain View, CA</name>
                    } else
                    if (responseNodeName.equalsIgnoreCase(TAG_Status)) {
                        // <Status> ... </Status>
                        NodeList statusNodes = responseElem.getChildNodes();
                        for (int st = 0; st < statusNodes.getLength(); st++) {
                            Node statusNode = statusNodes.item(st);
                            if (!(statusNode instanceof Element)) { continue; }
                            Element statusElem = (Element)statusNode;
                            String statusNodeName = statusElem.getNodeName();
                            if (statusNodeName.equalsIgnoreCase(TAG_code)) {
                                errCode = StringTools.trim(GoogleGeocodeV2.GetNodeText(statusElem));  // expect "200"
                                break; // we only care about the 'code'
                            }
                        }
                    } else
                    if (responseNodeName.equalsIgnoreCase(TAG_Placemark)) {
                        // <Placemark> ... </Placemark>
                        String id = responseElem.getAttribute(ATTR_id);
                        if ((id != null) && id.equals("p1")) {
                            NodeList placemarkNodes = responseElem.getChildNodes();
                            for (int pm = 0; (geoPoint == null) && (pm < placemarkNodes.getLength()); pm++) {
                                Node placemarkNode = placemarkNodes.item(pm);
                                if (!(placemarkNode instanceof Element)) { continue; }
                                Element placemarkElem = (Element)placemarkNode;
                                String placemarkNodeName = placemarkElem.getNodeName();
                                if (placemarkNodeName.equalsIgnoreCase(TAG_Point)) {
                                    NodeList pointNodes = placemarkElem.getChildNodes();
                                    for (int ptn = 0; (geoPoint == null) && (ptn < pointNodes.getLength()); ptn++) {
                                        Node pointNode = pointNodes.item(ptn);
                                        if (!(pointNode instanceof Element)) { continue; }
                                        Element pointElem = (Element)pointNode;
                                        String pointNodeName = pointElem.getNodeName();
                                        if (pointNodeName.equalsIgnoreCase(TAG_coordinates)) {
                                            String ll[] = StringTools.split(GoogleGeocodeV2.GetNodeText(pointElem),',');
                                            if (ll.length >= 2) {
                                                double lon = StringTools.parseDouble(ll[0],0.0); // longitude is first
                                                double lat = StringTools.parseDouble(ll[1],0.0);
                                                if (GeoPoint.isValid(lat,lon)) {
                                                    geoPoint = new GeoPoint(lat,lon);
                                                    break; // we only care about the 'GeoPoint'
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        } else {
                            //Print.logInfo("Skipping Placemark ID = %s", id);
                        }
                    }
                }
            }
        }

        /* create address */
        if (geoPoint != null) {
            // GeoPoint found
            return geoPoint;
        }

        /* check for Google reverse-geocode limit exceeded */
        if ((errCode != null) && errCode.equals("620")) {
            Print.logError("!!!! Google Reverse-Geocode Limit Exceeded [Error 620] !!!!");
        }

        /* no reverse-geocode available */
        return null;

    }

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    protected static Document GetXMLDocument(String url, int timeoutMS)
    {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            InputStream input = HTMLTools.inputStream_GET(url, timeoutMS);
            InputStreamReader reader = new InputStreamReader(input, ENCODING_UTF8);
            InputSource inSrc = new InputSource(reader);
            inSrc.setEncoding(ENCODING_UTF8);
            return db.parse(inSrc);
        } catch (ParserConfigurationException pce) {
            Print.logError("Parse error: " + pce);
            return null;
        } catch (SAXException se) {
            Print.logError("Parse error: " + se);
            return null;
        } catch (IOException ioe) {
            Print.logError("IO error: " + ioe);
            return null;
        }
    }

    // ------------------------------------------------------------------------

    /* return the value of the XML text node (never null) */
    protected static String GetNodeText(Node root)
    {
        StringBuffer sb = new StringBuffer();
        if (root != null) {
            NodeList list = root.getChildNodes();
            for (int i = 0; i < list.getLength(); i++) {
                Node n = list.item(i);
                if (n.getNodeType() == Node.CDATA_SECTION_NODE) { // CDATA Section
                    sb.append(n.getNodeValue());
                } else
                if (n.getNodeType() == Node.TEXT_NODE) {
                    sb.append(n.getNodeValue());
                } else {
                    //Print.logWarn("Unrecognized node type: " + n.getNodeType());
                }
            }
        }
        return sb.toString();
    }

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    private static final String ARG_ACCOUNT[]       = new String[] { "account", "a"  };
    private static final String ARG_GEOCODE[]       = new String[] { "geocode", "gc" };
    private static final String ARG_REVGEOCODE[]    = new String[] { "revgeo" , "rg" };

    private static String FilterID(String id)
    {
        if (id == null) {
            return null;
        } else {
            StringBuffer newID = new StringBuffer();
            int st = 0;
            for (int i = 0; i < id.length(); i++) {
                char ch = Character.toLowerCase(id.charAt(i));
                if (Character.isLetterOrDigit(ch)) {
                    newID.append(ch);
                    st = 1;
                } else
                if (st == 1) {
                    newID.append("_");
                    st = 0;
                } else {
                    // ignore char
                }
            }
            while ((newID.length() > 0) && (newID.charAt(newID.length() - 1) == '_')) {
                newID.setLength(newID.length() - 1);
            }
            return newID.toString();
        }
    }

    /**
    *** Main entery point for debugging/testing
    **/
    public static void main(String argv[])
    {
        RTConfig.setCommandLineArgs(argv);
        Print.setAllOutputToStdout(true);
        Print.setEncoding(ENCODING_UTF8);
        String accountID = RTConfig.getString(ARG_ACCOUNT,"demo");
        GoogleGeocodeV2 gn = new GoogleGeocodeV2("google", null, null);

        /* reverse geocode */
        if (RTConfig.hasProperty(ARG_REVGEOCODE)) {
            GeoPoint gp = new GeoPoint(RTConfig.getString(ARG_REVGEOCODE,null));
            if (!gp.isValid()) {
                Print.logInfo("Invalid GeoPoint specified");
                System.exit(1);
            }
            Print.logInfo("Reverse-Geocoding GeoPoint: " + gp);
            Print.sysPrintln("RevGeocode = " + gn.getReverseGeocode(gp,null/*localeStr*/,false/*cache*/));
            // Note: Even though the values are printed in UTF-8 character encoding, the
            // characters may not appear to be properly displayed if the console display
            // does not support UTF-8.
            System.exit(0);
        }

        /* no options */
        Print.sysPrintln("No options specified");
        System.exit(1);

    }

}

redpajama_set_name: RedPajamaGithub


text:
Rangam Consultants Inc
PPP Loan Data — Rangam Consultants Inc, Somerset, NJ
Industry: Temporary Help Services
Location: Somerset, NJ
Rangam Consultants Inc is a limited liability company (LLC) located at 270 Davidson Avenue Suite 103 in Somerset, New Jersey that received a Coronavirus-related PPP loan from the SBA of $4,080,000.00 in April, 2020.
Fulton Bank, National Association
Rangam Consultants Inc in Somerset, NJ received a Paycheck Protection Loan of $4.08M through Fulton Bank, National Association, which was approved in April, 2020.
For Rangam Consultants Inc, the calculation used to estimate payroll costs is shown below:
Based on the standard PPP eligibility formula, it may be possible to estimate the payroll expenses represented by a company on their PPP application (see details above). In order to qualify for the PPP loan amount received, Rangam Consultants Inc's 2019 payroll expenses are estimated to be at least $19.58M.
On the PPP application, Rangam Consultants Inc reported intending to use the proceeds of their PPP loan for the following expenses:
Mortgage Interest: $1,020,000
Because the loan received by Rangam Consultants Inc is at least $2M dollars, it is subject to a full review by the SBA to ensure eligibility and compliance with PPP program requirements.
Business Information - Rangam Consultants Inc in Somerset, NJ
270 Davidson Avenue Suite 103
Somerset, NJ 8873
Temporary Help Services
Somerset County, NJ — SBA Office 0299 — Congressional District: NJ-12
Similar Companies near Somerset
In the Somerset area, 4 businesses in the "Temporary Help Services" industry received a PPP loan. These local businesses reported an average of 192 employees (compared to this company's 272) and received an average PPP loan of $2,458,610 (compared to this company's $4.08M).
Sister2Sister Inc
$4,439 PPP Loan
Net2Source Inc
$2M PPP Loan
Nationwide, 8,904 businesses in the "Temporary Help Services" industry received a total of $3.65B in PPP loans. This industry in total received less than 1% of the total PPP funding distributed.
PPP recipients in this industry report an average of 51 employees, 81% lower than Rangam Consultants Inc's reported 272 employees, and received an average PPP loan of $409,706, 90% lower than this company's loan of $4.08M.
Source: www.federalpay.org/paycheck-protection-program/rangam-consultants-inc-somerset-nj

redpajama_set_name: RedPajamaCommonCrawl


Qiuli Fu; Generation of lentoid bodies from patient-specific induced pluripotent stem cells derived from urinary cells. Invest. Ophthalmol. Vis. Sci. 2018;59(9):2608.
Presentation Description : More than 300 congenital cataract (CC)-linked mutations in vary genes have been discovered during the past few decades. However, the lack of appropriate human derived disease models has made it difficult to understand their pathological mechanisms, which has limited researches on curing the disease. Recent reports showed that patient-specific models using human-induced pluripotent stem cells (hiPSCs) could be ideal disease models not only for pathological mechanism investigation but also for research on new clinical drug targets. Human lens models in vitro were first established, in which lentoid bodies (LBs) with a human lens-like transparent structure were generated from human urinary cell derived iPSCs (UiPSCs) using the "fried egg" method. The generated LBs not only showed lens biological properties but also were large and transparent enough for the analysis of optical characteristics, suggesting that the generation of patient-specific LBs could be used in future investigations of cataractogenesis. Therefore, patient-specific iPSCs from CC patients with known mutations were then generated and differentiated into the relevant LBs, in which the cataract process was mimicked. Biological properties and optical characteristics between LBs from normal UiPSCS and LBs from patient-specific UiPSCs were compared to address the underlying mechanism of CC. In conclusion, the LBs that generated from patient-specific iPSCs not only mimicked the vivo process of cataracts, but also avoided the species differences that annoyed researchers for a long time. Thus, it is an ideal disease model for studies of cataractogenesis.


text:
VISITOR MEMBER GOLD MEMBER LIFE MEMBER Username : Password : LOGIN
Your status >> REGISTER --- ---
Welcome to Around People Finder,
(UK, USA, Australia, New Zealand, Europe and World-wide)
Always wanted to find your old school friends ? Lost contact with family members ?
Wondered what happened to those old work colleagues ? Army Friends ? Neighbours ?
In fact using Around, you can start looking for all of these under one site.
With a powerful search engine, over 10 million searches have been made to-date !
The site contains - School (43,000 UK - 13,000 Australian and over 2,800 New Zealand Schools) Colleges, Services, Home Town, Postcode, Groups, Colleague, Surnames, Businesses...
post missing persons messages, plus much much more...
It only takes a minute to register and its !
Click here to register login at the top with your username/password
(DOB 1969) Canada
Roy Robert Hammond
(DOB 1939) - Surrey England/ United Kingdom
Tall - 6ft-5in. Isleworth Town & Grammar schools to Imperial College to GEC to DSC. Retired in 1997 after career in the telecommunications business. 16 years resident overseas (Canada, Australia, Malta, Taiwan and Brazil). Returned to `settle down` in 1979. Very happily married since 1967: two children. Contact via this site. Now living in Lightwater, Surrey.||LOOK
ROGER GOSSOP
> (DOB 1943) - llanon/ United Kingdom
> (DOB 1968) - Truro/ United Kingdom
If you know me, get in touch, I have begun a new life and I am looking for my Soul Mate, you know who you are. Please get in touch, I really need you
Dorian Hamer
> (DOB 1965) - Cheltenham/ United Kingdom
I can`t think that there are too many Dorian Hamer`s in the world so if you looking for him it`s probably me... White Male, 5`9``, brown hair/eyes and used to live in Meribel Ski area, France
Ursula McArdle nee McKay
(DOB 1949) - Hunwick/ United Kingdom
(DOB 1987) - Abuja/ Nigeria
dark, tall and a faint mark on my fore head.
Lorraine Dormer
> (DOB 1944) Ireland
The Dancing Queen!
d clark
> (DOB 1961) - walsall/ United Kingdom
gill garrett
> (DOB 1943) - enfield middlesex/ United Kingdom
joanne erriker
> (DOB 1984) - gloucester/ United Kingdom
Ian W. Kirby
(DOB 1983) - Leicester/ United Kingdom
freddie rye
> (DOB 1965) - kent/ United Kingdom
> - Nuneaton/ United Kingdom
hello mate. I was talking to the guys in the office about the `wet knee club` and my fake wedding, so I though i`d google some of you. You were first up! my contact address is rsmith pattonair.com give us a mail. the guys in plymouth are rubbish at keeping in touch but I contact DAve Maskell in OZ sometimes.
Etive Hickson
> (DOB 1942) - Argyll/ United Kingdom
(DOB 1985) - senegal/ Senegal
i will tell you later i will tell you later i will tell you later i will tell you later i will tell you later
Chondra Brice
> (DOB 1979) - rock hill/ United States
louise wrigley (ashcroft)
(DOB 1978) - hindley wigan/ United Kingdom
im married with children
i am married with children i used to attend mornington high school hindley from 89 to 94.
Last Updated : 7/19/2019
.........................................................................................................................................................................................................
BLINDSKUNK
www.ifooty.com
FBI Dossier
Google+ Makeover
LOST SOULS ADDRESS BOOK QUIZZER MANYFEET GOOGLE+ MAKEVOER
WINKI DIRECTORY (C) 1999-2019 x


Alison is an enthusiastic maker, software and firmware engineer, entrepreneur, and goat farmer. Her software has supported audio hardware in many Apple products of the last decade, including Macs, iPhone and iPad, and the Apple Cinema Display. Last year she co-founded Beatrice Forms, where she developed a smartphone 3D body scanning technology to fabricate custom dress forms, a tool used by apparel designers to fit clothing for real bodies.


For travel between the ceremony and reception we recommended using car sharing services such as Uber and Lyft or old fashioned NYC yellow taxis.
Parking: there is often street parking available. Two parking garages nearby include Champion Parking (60 East 90th Street) and Park Regis (50 East 89th Street).


European envoys in Sudan on Monday called for the creation of confidence-building measures in the Nuba Mountains to capitalise on the current, localised cease-fire in the region.
"EU Heads of Mission agreed that there was an urgent need for confidence-building measures, such as direct talks between political leaders and contacts at grassroot levels," the envoys said in a statement released by the German embassy in the Sudanese capital, Khartoum, on 18 February.
An agreement to implement a cease-fire in the Nuba Mountains region of Southern Kordofan, south-central Sudan, was signed by representatives of the Sudanese government and the rebel Sudan People's Liberation Movement/Army (SPLM/A) in Burgenstock, Switzerland, on 19 January, and came into force 72 hours later.
Broad humanitarian access to the Nuba Mountains was one of four confidence-building measures proposed by US peace envoy John Danforth during a visit to Sudan in November. The other three concerned: a cessation of bombing and artillery attacks on civilians; an undertaking to end slavery; and, the creation of zones and times of tranquility (throughout Sudan) in which humanitarian assistance could be offered to vulnerable populations.
The Nuba cease-fire agreement, set to run for an initial period of six months, is due to be supervised by a Joint Military Commission (JMC) and an International Monitoring Unit (IMU), the latter comprising 10 to 15 military and civilian personnel from Western Europe and North America.
Although neither the JMC nor the IMU have yet been deployed, the EU Heads of Missions said that Nuba the cease-fire had "so far, on the whole, been respected by both sides."
They added that support should now be focused on "moving the cease-fire into a comprehensive and sustainable peace with [community] ownership."
During their visit to both government-held and rebel-controlled areas of the Nuba Mountains on 14 and 15 February, EU Heads of Missions in Sudan held talks with the Governor of Southern Kordofan State, Majzub Yousif Babiker, with tribal leaders in the town of Kadugli, and with a senior commander of the SPLA, the statement said.
The EU Heads of Missions called for a rapid response from the international community to "cope with the urgent needs of the most vulnerable parts of the population" in the region.
This, they said, should include delivery of food aid, tools and seeds, water pumps and medicine, as well as the deployment of the JMC to "preserve the positive development following the signing of the Cease-Fire Agreement for the Nuba Mountains."


We'll see you all in January when we will kick the new year off with some brand new Book Two art.
PS! Click on the image for a higher-resolution version. There are hints to the future within.
Only two stockings by the fireplace...Dad and Saga???
I now see that there is another thread about the Christmas card in the spoiler subforum.
So maybe it is a good idea that speculations are discussed there instead?
And 2015 is setting up to be a *great* year games-wise.
I mean, we get the finish of DFC, we get a new Mass Effect and then there's this: Life Is Strange. If the story in this one turns out to be as good as the one in Remember Me (also by Dontnod), it's going to be great.
This means that we're still far away from release of Book Two, So Febuary 2015?
As I've already posted elsewhere, I would not be surprised at all if it's not out until February.
Oh great. Would have been nice to actually GET the Christmas card/Kickstarter update.
I'm little dissapointed that Book Two isn't released in December 2014. I had a hope for that release. Now it seems that it won't release even in January 2015 so maybe Febuary 2015. If this will repeat for future books Book Five won't release in September 2015.
Well, we all know that video game target release dates are among the most ephemeral of entities.
Did it not go out to Paypal backers?
Can't say – I only know I didn't get it.
Seven hours after I should have, that usually means that the usual people were forgotten.
I'm guessing it may have been either an oversight or a glitch that wasn't noticed with the holidays, but that's not good. :/ ...I think I'll reply to the tweet they posted about it and mention it, if it looks like no one else has. I don't know if they'll see it, but it should still be brought to their attention.
I read that to mean RTG will share more art from Book 2 in early January.
Were we supposed to get an email? I did get one about the KickStarter update. Is that the one?
Yeah. Whenever they send out a Kickstarter update, you should get an email from Kickstarter, unless you've unsubscribed from updates. However, they use another service (Mail Chimp, I believe) to send out the same message to anyone who backed via PayPal, as they don't get the automatic updates through KS.
Right. Got it. I was in the same situation for the Tex Murphy: The Tesla Effect KS campaign. I missed the campaign close by 4 days. So, I backed via PayPal. Occasionallly, an update would come out on KS that didn't get sent to PP backers. I took it in stride: it's a small team, with a tremendous amount to do, so I took it upon myself to keep up to date by visiting KS, checking the BFG forums, and The Unofficial Tex Murphy web site. I figured, "I'm a backer so, in effect, I'm an investor, part of the team, so I should do my part to help out."
I get what you're saying, Ikon. It does happen. It's a small team, etc.
But RTG should still be made aware of it so that backers who maybe don't follow them across the internet get the email. That's all. It's happened a few times at this point where an update didn't get pushed out to PP backers. Which happens, but it's still frustrating if you're a PP backer and it's still fair feedback for RTG.
And not everyone who backs a game wants to have to track the team all over the internet. And if they send out a message that's meant to go to all backers, it should go to all backers. If PP backers were totally missed, I'm sure it was a total accident. But again... no harm in making RTG aware.
You know, after the Tex Murphy experience, I had an idea that it might be good to bring a loyal fan into the mix and make them the communications staffer. I'm sure many fans would be happy to do the job pro bono. That way, the developers would only have to create the update and send it to one person who, in turn, would put it through a process that would send out all backers.
It would save money by not having a paid staffer spend time doing the notifications, and it would, I believe, be a more reliable way to send out updates/notifications. There is nothing like a loyal fan to make something work.
I think this is the single greatest failing of KS campaigns; they don't utilize the talent available to them to the best effect. I've heard the counter-arguments; I don't believe them.
RTG used to have a dedicated community manager, but for various reasons it didn't work out. On the one hand, us moderators are basically unpaid volunteers. On the other hand, having an unpaid volunteer pushing out updates to various services could be very bad. It would require that person to have access to all of the accounts, which is a risky proposition for a non-employee. There are also some potential ethical issues here as well.
I see what you're saying, but that just seems risky and more trouble than it's worth for RTG, at least right now. Also, from what I've gathered, it doesn't take that long for them to send out the email to the PP backers. Based on what Rakel said in the past, though, it's just something Ragnar forgets to do when he does it. And I wouldn't be surprised if he was the one taking the time to push out the updates on Christmas Eve.
Like I said, I've heard the counter arguments; I believe all the issues can be oversome, and rather easily in fact.


text:
The uncritical realism of realist evaluation
Sam Porter
This article is a response to Ray Pawson's critique of critical realism, the philosophy of science elaborated by Roy Bhaskar. I argue with Pawson's interpretation of critical realism's positions on both natural and social science and his charges concerning its totalizing ontology, its arrogant epistemology and its naive methodology. The differences between critical realism and realist evaluation are not as significant as Pawson contends. The main differences between the two realisms lie in their approaches to the relationship between social structures and human agency, and between facts and values. I argue that evaluation scientists need to clearly distinguish structure and agency. They should also make their values explicit. The uncritical approach of realist evaluation, combined with its underplaying of the importance of agency, leaves it open to implication in the abuses of bureaucratic instrumentalism.
critical realism, naturalism, philosophy of science, Ray Pawson, realistic evaluation, Roy Bhaskar
© The Author(s) 2015
Fingerprint Dive into the research topics of 'The uncritical realism of realist evaluation'. Together they form a unique fingerprint.
evaluation Earth & Environmental Sciences
social structure Earth & Environmental Sciences
natural science Earth & Environmental Sciences
philosophy of science Social Sciences
natural sciences Social Sciences
ontology Social Sciences
epistemology Social Sciences
Porter, S. (2015). The uncritical realism of realist evaluation. Evaluation, 21(1), 65-82. https://doi.org/10.1177/1356389014566134
Porter, Sam. / The uncritical realism of realist evaluation. In: Evaluation. 2015 ; Vol. 21, No. 1. pp. 65-82.
@article{82a2d1dfb5c948bf8402f90964abaf66,
title = "The uncritical realism of realist evaluation",
abstract = "This article is a response to Ray Pawson{\textquoteright}s critique of critical realism, the philosophy of science elaborated by Roy Bhaskar. I argue with Pawson{\textquoteright}s interpretation of critical realism{\textquoteright}s positions on both natural and social science and his charges concerning its totalizing ontology, its arrogant epistemology and its naive methodology. The differences between critical realism and realist evaluation are not as significant as Pawson contends. The main differences between the two realisms lie in their approaches to the relationship between social structures and human agency, and between facts and values. I argue that evaluation scientists need to clearly distinguish structure and agency. They should also make their values explicit. The uncritical approach of realist evaluation, combined with its underplaying of the importance of agency, leaves it open to implication in the abuses of bureaucratic instrumentalism. ",
keywords = "critical realism, naturalism, philosophy of science, Ray Pawson, realistic evaluation, Roy Bhaskar",
author = "Sam Porter",
journal = "Evaluation",
Porter, S 2015, 'The uncritical realism of realist evaluation', Evaluation, vol. 21, no. 1, pp. 65-82. https://doi.org/10.1177/1356389014566134
The uncritical realism of realist evaluation. / Porter, Sam.
In: Evaluation, Vol. 21, No. 1, 01.2015, p. 65-82.
T1 - The uncritical realism of realist evaluation
AU - Porter, Sam
N2 - This article is a response to Ray Pawson's critique of critical realism, the philosophy of science elaborated by Roy Bhaskar. I argue with Pawson's interpretation of critical realism's positions on both natural and social science and his charges concerning its totalizing ontology, its arrogant epistemology and its naive methodology. The differences between critical realism and realist evaluation are not as significant as Pawson contends. The main differences between the two realisms lie in their approaches to the relationship between social structures and human agency, and between facts and values. I argue that evaluation scientists need to clearly distinguish structure and agency. They should also make their values explicit. The uncritical approach of realist evaluation, combined with its underplaying of the importance of agency, leaves it open to implication in the abuses of bureaucratic instrumentalism.
AB - This article is a response to Ray Pawson's critique of critical realism, the philosophy of science elaborated by Roy Bhaskar. I argue with Pawson's interpretation of critical realism's positions on both natural and social science and his charges concerning its totalizing ontology, its arrogant epistemology and its naive methodology. The differences between critical realism and realist evaluation are not as significant as Pawson contends. The main differences between the two realisms lie in their approaches to the relationship between social structures and human agency, and between facts and values. I argue that evaluation scientists need to clearly distinguish structure and agency. They should also make their values explicit. The uncritical approach of realist evaluation, combined with its underplaying of the importance of agency, leaves it open to implication in the abuses of bureaucratic instrumentalism.
KW - critical realism, naturalism, philosophy of science, Ray Pawson, realistic evaluation, Roy Bhaskar
JO - Evaluation
JF - Evaluation
Porter S. The uncritical realism of realist evaluation. Evaluation. 2015 Jan;21(1):65-82. https://doi.org/10.1177/1356389014566134


text:
Sedum hirsutum é uma espécie de planta com flor pertencente à família Crassulaceae.

A autoridade científica da espécie é All., tendo sido publicada em Flora Pedemontana 2: 122, t. 65, f. 5. 1785.

O seu nome comum é uva-de-gato.

Portugal

Trata-se de uma espécie presente no território português, nomeadamente os seguintes táxones infraespecíficos:

 Sedum hirsutum subsp. hirsutum - presente em Portugal Continental. Em termos de naturalidade é nativa da região atrás referida. Não se encontra protegida por legislação portuguesa ou da Comunidade Europeia.

Bibliografia
 Checklist da Flora do Arquipélago da Madeira (Madeira, Porto Santo, Desertas e Selvagens) - Grupo de Botânica da Madeira
 Sedum hirsutum - Portal da Biodiversidade dos Açores
 Sedum hirsutum - The Plant List (2010). Version 1. Published on the Internet; http://www.theplantlist.org/ (consultado em 29 de setembro de 2014).
 Sedum hirsutum - International Plant Names Index
 Castroviejo, S. (coord. gen.). 1986-2012. Flora iberica 1-8, 10-15, 17-18, 21. Real Jardín Botánico, CSIC, Madrid.

 Ligações externas

 Sedum hirsutum - Flora-on
 Sedum hirsutum - The Euro+Med PlantBase
 Sedum hirsutum - Flora Vascular
 Sedum hirsutum - Biodiversity Heritage Library - Bibliografia
 Sedum hirsutum - JSTOR Global Plants
 Sedum hirsutum - Flora Europaea Sedum hirsutum - NCBI Taxonomy Database
 Sedum hirsutum - Global Biodiversity Information Facility
 Sedum hirsutum'' - Encyclopedia of Life

Flora de Portugal
hirsutum

redpajama_set_name: RedPajamaWikipedia


text:
Page: < 1 2 3 4 5 >
Eucharistic Prayer Cards
This handout provides each element of the Eucharistic Prayer on a separate card for students to arrange in order.
My Morning and Evening Prayers
This handout asks students to think about the Liturgy of the Hours before they answer questions about and create their own morning and evening prayers.
Healing Rain
The song/video "Healing Rain," by Michael W. Smith, available on YouTube (4:31), can be used for prayer on any day. It offers powerful imagery about God's healing love.
The song/video "Water of Life," by Michael W. Smith, available on YouTube (3:37), provides strong imagery and biblical text related to water. It is used for the opening prayer on day 2 and could be used again as the closing prayer for day 2.
The song/video "Behold the Lamb of God," by Matt Maher, available on YouTube (4:58), is used for opening prayer on day 2.
The song/video"Remembrance," by Matt Maher, available on YouTube (5:16), is good for opening prayer for any day.
Table of Grace
The song/video "Table of Grace," by Phillips, Craig, and Dean, available on YouTube (4:46) with lyrics, is suitable for any day.
Thy Word
The song/video "Thy Word," by Amy Grant and Michael W. Smith, available on YouTube (3:13), is a setting of Psalm 119 with the appropriate refrain of "Thy word is a lamp unto my feet and a light unto my path" (v. 105, KJV). The sheet music can also be found in many congregational hymnals. It is appropriate for use on day 1.
Remembrance (Communion Song)
The song/video "Remembrance (Communion Song)," by Matt Redman, available on YouTube (5:47), is recommended for prayer on any day.
Call It Grace
The song/video "Call It Grace," by Unspoken, available on YouTube (4:00), is appropriate for use on day 3.
The song/video "Do Something," by Matthew West, available on YouTube (4:05), is appropriate for use on day 2.
Open the Eyes of My Heart
The song/video "Open the Eyes of My Heart," available on YouTube (4:13), is appropriate for prayer on any day.
Open My Eyes
The song/video "Open My Eyes," by Jesse Manibusan, available on YouTube (2:54) with lyrics and as a live performance (3:05), used for prayer on day 2, invites us to be aware of the presence of God. The song is included in many parish hymnals as well.
When We Gather
The song/video "When We Gather," by Brad and Rebekah, available on YouTube (4:04) with lyrics, is appropriate for prayer on any day.
The song/video "City of God," by Dan Schutte, available on YouTube (3:11), complements the theme of day 4, and provides lyrics and visuals for reflection.
The song/video "Holy, Holy, Holy," by Steven Curtis Chapman, available on YouTube (4:54), is a contemporary version of the classic hymn and can be used for prayer or meditation on day 2.
The song/video "Don't Give Up," by Calling Glory, available on YouTube (3:54) with lyrics, emphasizes persistence in faith as a prayer focus throughout the week.


text:
Ugetsu (1953) Film Review – Feudal-Era Horror
Posted on January 2, 2023 by Wesley Sistrunk
It's understandable why Japanese filmmakers focus so often on the feudal era in their horror cinema. It's a setting so naturally horrific in the plight and pain of the peasant class that few supernatural elements are necessary to invoke dread in audiences. The stark reality of daily life alone is enough to make the viewer cringe and their skin crawl.
The cruel irony of Ugetsu (1953), directed by Kenji Mizoguchi, is that these moments of reality are its harshest and scariest. The film is a ghost story with no sense of danger and a villain that lacks intrigue. The concern lies with the characters escaping their real-life horrors and not the spirit that offers one a better alternative.
It opens with an enemy army raiding an impoverished village, where Genjuro, a potter with lofty ambitions of upward mobility, desperately works to keep his kiln lit before the soldiers arrive. His neighbors flee from the rapidly approaching torches to the safety of the forest, as the marauders enslave the men and rape the women in their path. But Genjuro is steadfast in remaining. His wife pulls at his clothes, pleading with him to leave, but if the fire goes out, they'll be ruined. The tension in this scene is palpable, and it surrounds the fate of some clay pots.
Genjuro's brother-in-law, Tobei, also entertains his own grand delusions. He fancies himself a samurai, and he begs to be made a vassal of the local warlord. The actual samurai instead mock and beat him for his troubles. Like Genjuro, he sees the pottery business as a means to an end: with enough money, he can buy his own armor and a fancy spear. Fast-forwarding to the second act, his dreams actually come true. He affords his prize after a successful day of business in the city, and he is made a noble after presenting the warlord with the stolen head of a rival general.
In chasing his fortune, however, he leaves his disapproving wife to return home alone. Out on the road, a group of soldiers accost her, push her to the ground, and drag her into a hut. Her rape is only implied but is no less horrific, as the soldiers absolve their sins with coins they drop on the floor. The whole sequence is brutally direct and dispiriting, the largest imprint left by the film.
But this bluntness comes at a cost. The trials of Tobei and his wife are meant only as the subplot, yet they steal the entire show. Genjuro's story, meanwhile, leaves much to be desired. In the city, he meets a noblewoman and her handmaiden when they purchase a large portion of his stock. They instruct him to bring the goods to her country manor, where he will receive his payment. Upon his arrival, things quickly become romantic, and the lonely noblewoman soon asks for his hand in marriage. The whirlwind affair excites Genjuro, and he, like Tobei, leaves his wife to fend for herself.
The reveal is hardly shocking. The noblewoman is, in fact, a long-dead spirit that has returned to experience the love that she never had in life, and the scattered hints of her true nature aren't exactly subtle. There's little to process once a priest divulges the twist to Genjuro (and the audience) near the story's climax. The film commits the cardinal sin of telling instead of showing, and this lack of nuance equates to a lack of interest.
Is there a moral to the tale? It's hard to see one through the tangled plot. It is not explicitly anti-capitalist, despite its cautionary tale of greed, and any subtext related to war lacks focus. If anything, it seems to rail against personal aspiration, a particularly unsatisfying conclusion. I'm sure that there's a cultural element that I'm missing here, which is, of course, no fault of the film or its director. It leaves me at a loss nonetheless.
Ugetsu is not a bad film. It's simply an unremarkable one. The narrative flows freely, but its execution is clunky. Moments of dread are offset by a meandering horror story that isn't horrifying, and the whole presentation would have been better served if left as a historical drama. It's crushing ending, as effective a scene as the attack on Tobei's wife, is the only supernatural aspect that truly works in its favor. But the weight of it alone is not enough to offset the preceding ninety minutes.
Then again, its success instigated an entire movement. It won multiple awards such as the Silver Lion Award in Venice, and it opened Western audiences to the world of Japanese horror. Alas, these are only this reviewer's personal opinions and are hardly facts set in stone.
Blast (2021) Film Review – An Explosive French Thriller
Blast, or Deflagrations in its cooler sounding, original French title, is a tense thriller focusing on a small company that disarms landmines around the world. After a recently successful contract…
DEAD SNOW Review (2009) – An Entertaining Bloodbath!
The horror genre has been flooded with a ton of films and sometimes certain gems can slip through the cracks, and go unseen for a long time. Enter "Dead Snow"….
Koreatown Ghost Story (2021) Short Film Review
Konnichiwa! Dia Duit! Howya! Gather round children, it's time for Straight Outta Kanto to tell you a ghost story… Koreatown Ghost Story to be precise. Written, directed and produced by…
Superhost (2021) Film Review – Host-Mortem
Though the pandemic has seriously affected the way the world conducts business and we live our lives, filmmakers have been working to find a way to keep creating films in…
Hellraiser (2022) Film Review – A Reconfiguration of Clive Barker's Harrowing Vision
Hellraiser fans rejoiced when the news broke out that Clive Barker would be regaining the U.S. rights to his novella The Hellbound Heart and the film it spawned in December…
Posted in Asian Film, The YureiTagged Asian Horror, film review, Folk Horror, Horror, Horror Film, J-Horror, Japanese Cinema, Supernatural Horror
← Recent Reads: Cherish Farrah and Other Reads
Perfect Blue (1991) Book Review – Thank God for Satoshi Kon →