com.ikanow.infinit.e.harvest.enrichment.custom.GeoReference.java Source code

Java tutorial

Introduction

Here is the source code for com.ikanow.infinit.e.harvest.enrichment.custom.GeoReference.java

Source

/*******************************************************************************
 * Copyright 2012, The Infinit.e Open Source Project.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package com.ikanow.infinit.e.harvest.enrichment.custom;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang.WordUtils;

import com.google.gson.reflect.TypeToken;
import com.ikanow.infinit.e.data_model.store.DbManager;
import com.ikanow.infinit.e.data_model.store.feature.geo.GeoFeaturePojo;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;

/**
 * GeoReference
 * @author cvitter
 */
public class GeoReference {
    // Private class variables
    private static HashMap<GeoFeaturePojo, List<GeoFeaturePojo>> _cache = new HashMap<GeoFeaturePojo, List<GeoFeaturePojo>>();
    private static GeoFeaturePojo geoInfo = null;

    /**
     * enrichGeoInfo
     * @param geoInfo
     * @param exactMatchOnly
     * @param hasGeoindex
     * @return
     */
    public synchronized static List<GeoFeaturePojo> enrichGeoInfo(GeoFeaturePojo geoInfo, Boolean exactMatchOnly,
            Boolean hasGeoindex) {
        return enrichGeoInfo(geoInfo, exactMatchOnly, hasGeoindex, -1);
    }

    /**
     * enrichGeoInfo
     * @param geoInfo
     * @param exactMatchOnly
     * @param hasGeoindex
     * @param nMaxReturns
     * @return
     */
    public synchronized static List<GeoFeaturePojo> enrichGeoInfo(GeoFeaturePojo g, Boolean exactMatchOnly,
            Boolean hasGeoindex, int nMaxReturns) {
        geoInfo = g;

        BasicDBObject query = null;
        DBCursor result = null;
        try {
            // Get cached values and return if the geoInfo object passed in matches a cached value
            List<GeoFeaturePojo> cachedVal = _cache.get(geoInfo);
            if (null != cachedVal) {
                return cachedVal;
            }

            // Establish the collection manager object use to connect to MongoDB
            // The connection will persist for lifetime of import
            DBCollection geoDb = DbManager.getFeature().getGeo();

            // If only search_field has been provided set exactMatchOnly = true
            if ((geoInfo.getSearch_field() != null) && (geoInfo.getCity() == null) && (geoInfo.getRegion() == null)
                    && (geoInfo.getCountry() == null)) {
                exactMatchOnly = true;
            }

            // Exact match
            if (exactMatchOnly) {
                query = getQuery(hasGeoindex, 1);
                //DEBUG
                //System.out.println(query);
                result = getGeoReference(geoDb, query, nMaxReturns);
            }
            // Loose match, broaden/modify search on each of up to 4 attempts
            else {
                for (int i = 1; i <= 4; i++) {
                    query = getQuery(hasGeoindex, i);
                    if (null != query) {
                        result = getGeoReference(geoDb, query, nMaxReturns);
                        if (result.hasNext()) {
                            break;
                        }
                    }
                }
            }

            if (result.hasNext()) {
                List<GeoFeaturePojo> gpl = GeoFeaturePojo.listFromDb(result, GeoFeaturePojo.listType());
                _cache.put(geoInfo, gpl);
                return gpl;
            }
            //No value returned, cache a null value so we don't waste time searching for this value again
            else {
                _cache.put(geoInfo, null);
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * getQuery
     * @param g
     * @param hasGeoindex
     * @param attempt
     * @return
     */
    //TODO (INF-1864): running this in non-strict mode can cripple the DB since search field might not
    //be set ... at least need to cache such queries (almost always the US every time!)....

    private static BasicDBObject getQuery(Boolean hasGeoindex, int attempt) {
        BasicDBObject query = new BasicDBObject();

        // SearchField
        String searchField = (geoInfo.getSearch_field() != null) ? geoInfo.getSearch_field().toLowerCase() : null;

        // Cities are all lower case in the georeference collection, set toLowerCase here
        String city = (geoInfo.getCity() != null) ? geoInfo.getCity().toLowerCase() : null;

        // Use WordUtils.capitalize to set first char of region and country words to Upper Case
        String region = (geoInfo.getRegion() != null) ? WordUtils.capitalize(geoInfo.getRegion()) : null;
        String country = (geoInfo.getCountry() != null) ? WordUtils.capitalize(geoInfo.getCountry()) : null;
        String countryCode = geoInfo.getCountry_code();

        // If the only field sent was the search_field
        if ((searchField != null) && (city == null) && (region == null) && (country == null)
                && (countryCode == null)) {
            query.put("search_field", searchField);
        }

        // Otherwise...
        else {
            switch (attempt) {
            case 1:
                // Set the searchField if it is null
                if (searchField == null && city != null)
                    searchField = city.toLowerCase();
                if (searchField == null && region != null)
                    searchField = region.toLowerCase();
                if (searchField == null && country != null)
                    searchField = country.toLowerCase();

                // 
                if (searchField != null)
                    query.put("search_field", searchField);
                if (city != null)
                    query.put("city", city);
                if (region != null)
                    query.put("region", region);
                if (country != null)
                    query.put("country", country);
                if (null == searchField) { // only country code specified...
                    query.put("city", new BasicDBObject(DbManager.exists_, false));
                    query.put("region", new BasicDBObject(DbManager.exists_, false));
                }
                if (countryCode != null)
                    query.put("country_code", countryCode);
                break;

            case 2:
                if (city != null) {
                    query.put("search_field", city.toLowerCase());
                    query.put("city", city);
                } else if (region != null) {
                    query.put("search_field", region.toLowerCase());
                    query.put("region", region);
                } else {
                    query.put("search_field", country.toLowerCase());
                }

                if (country != null)
                    query.put("country", country);
                if (countryCode != null)
                    query.put("country_code", countryCode);
                break;

            case 3:
                if (searchField == null && region != null)
                    searchField = region.toLowerCase();
                if (searchField == null && country != null)
                    searchField = country.toLowerCase();

                if (searchField != null)
                    query.put("search_field", searchField);
                if (region != null)
                    query.put("region", region);
                if (country != null)
                    query.put("country", country);
                if (countryCode != null)
                    query.put("country_code", countryCode);
                break;

            default:
                if (country != null)
                    query.put("search_field", country.toLowerCase());
                if (country != null)
                    query.put("country", country);
                if (countryCode != null)
                    query.put("country_code", countryCode);
                break;
            }
        }
        if (query.isEmpty()) {
            return null;
        }

        // Only return records with GeoIndex objects
        if (hasGeoindex) {
            BasicDBObject ne = new BasicDBObject();
            ne.append(DbManager.exists_, true);
            query.put("geoindex", ne);
        }

        return query;
    }

    /**
     * getGeoReference
     * @param cm
     * @param query
     * @param nMaxReturns
     * @return
     */
    private static DBCursor getGeoReference(DBCollection geoDb, BasicDBObject query, int nMaxReturns) {
        if (nMaxReturns == -1) {
            return geoDb.find(query);
        } else {
            return geoDb.find(query).limit(nMaxReturns);
        }
    }

    public synchronized static void resetCache() {
        _cache.clear();
    }

    public synchronized static void reset() {
        resetCache();
    }

    /**
     * getNearestCity
     * Get the city closest to the lat/lon pair passed in
     * @param lat
     * @param lon
     * @return List<GeoReferencePojo>
     */
    public static List<GeoFeaturePojo> getNearestCity(DBCollection geoDb, String lat, String lon) {
        return getNearestCities(geoDb, lat, lon, 1);
    }

    /**
     * getNearestCities
     * Get n-cities near a lat/lon pair, results returned ordered by distance from
     * the lat/lon pair
     * @param lat
     * @param lon
     * @param nMaxReturns
     * @return List<GeoReferencePojo>
     */
    public static List<GeoFeaturePojo> getNearestCities(DBCollection geoDb, String lat, String lon,
            int nMaxReturns) {
        try {
            // Create Double[] from lat, lon
            Double[] d = new Double[] { Double.parseDouble(lat), Double.parseDouble(lon) };

            // Build query object to return the shell equivalent of:
            // db.georeference.find({geoindex : {$near : [lat.lon]}})
            BasicDBObject query = new BasicDBObject();
            BasicDBObject near = new BasicDBObject();
            near.append("$near", d);
            query.put("geoindex", near);

            // Perform query
            DBCursor result = geoDb.find(query).limit(nMaxReturns);

            // Convert results to List<GeoReferencePojo>
            List<GeoFeaturePojo> gpl = GeoFeaturePojo.listFromDb(result,
                    new TypeToken<ArrayList<GeoFeaturePojo>>() {
                    });
            return gpl;
        } catch (Exception e) {
            return null;
        }
    }

}