/******************************** LICENSE ********************************


 Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)
 
 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.


 ******************************** LICENSE ********************************/

/*! \file Proj4Projection.cc
    \brief Implementation of Proj4Projection.
    \author Meteorological Visualisation Section, ECMWF

    Started: Tue May 18 17:39:58 2010

*/

#include <Proj4Projection.h>
#include <Polyline.h>
#include <GridPlotting.h>
#include <LabelPlotting.h>
#include <Text.h>
#include "MetaData.h"
#include "MagConfig.h"

namespace magics
{

struct Epsg
{
	Epsg(const string& name) : name_(name) {
		epsgs_.insert(make_pair(lowerCase(name), this));
		methods_["definition"] = &Epsg::definition;
		methods_["bottom"] = &Epsg::bottom;
		methods_["right"] = &Epsg::right;
		methods_["left"] = &Epsg::left;
		methods_["top"] = &Epsg::top;
		methods_["lower_left"] = &Epsg::lowerleft;
		methods_["upper_right"] = &Epsg::upperright;
		methods_["grid_setting"] = &Epsg::set;
		methods_["lat_increment"] = &Epsg::lat_increment;
		methods_["lon_increment"] = &Epsg::lon_increment;
		methods_["simple"] = &Epsg::simple;
	}
	string name_;
	string definition_;


	typedef void (Epsg::*Method)(const json_spirit::Value&);
	map<string,  Method> methods_;

	void definition(const json_spirit::Value& value) {
		definition_ =  value.get_value<string>();
	}
	void lat_increment(const json_spirit::Value& value) {
		steplat_ =  value.get_value<double>();
	}
	void lon_increment(const json_spirit::Value& value) {
		steplon_ =  value.get_value<double>();
	}
	void simple(const json_spirit::Value& value) {
		latlonlabel_ =  (value.get_value<string>() == "on");
	}
	void bottom(const json_spirit::Value& value)
	{
		point(bottom_, value);
	}
	void top(const json_spirit::Value& value)
	{
		point(top_, value);
	}

	void left(const json_spirit::Value& value)
	{
		point(left_, value);
	}
	void right(const json_spirit::Value& value)
	{
		point(right_, value);
	}
	void upperright(const json_spirit::Value& value)
	{
		point(upperright_, value);
	}

	void lowerleft(const json_spirit::Value& value)
	{
		point(lowerleft_, value);
	}

	void point(GeoPoint& point, const json_spirit::Value& value) {
		assert (value.type() == json_spirit::obj_type);
		json_spirit::Object object = value.get_value< json_spirit::Object >();
		const json_spirit::Value lat = json_spirit::find_value(object, "lat");
		const json_spirit::Value lon = json_spirit::find_value(object, "lon");
		point.latitude(lat.get_value<double>());
		point.longitude(lon.get_value<double>());
	}



	GeoPoint bottom_;
	GeoPoint top_;
	GeoPoint left_;
	GeoPoint right_;

	GeoPoint lowerleft_;
	GeoPoint upperright_;

	double steplon_;
	double steplat_;
	bool latlonlabel_;

	static map<string, Epsg*> epsgs_;

	void  set(const json_spirit::Value&);

	static Epsg* find(const string& name) {
		map<string, Epsg*>::iterator epsg = epsgs_.find(lowerCase(name));
		if ( epsg == epsgs_.end() )  {
			MagLog::warning() << "Can not find information on " << name << ": use epsg instead" << endl;
			return find("EPSG:4326");
		}
		return epsg->second;
	}
	const char* definition() { return definition_.c_str(); }


};

class EpsgConfig : public MagConfig
{
public:
	EpsgConfig() {}
	~EpsgConfig() {}

	void callback(const string&, const json_spirit::Value&);
	void init();
	Epsg* epsg_;
};
}
using namespace magics;

map<string, Epsg*> Epsg::epsgs_;

void EpsgConfig::init()
{
	//methods_["epsg"] =  &EpsgConfig::epsg;
	MagConfigHandler(getEnvVariable("MAGPLUS_HOME") + MAGPLUS_PATH_TO_SHARE_ + "/epsg.json", *this);
}
void Epsg::set(const json_spirit::Value& value)
{
	assert (value.type() == json_spirit::obj_type);
	json_spirit::Object object =value.get_value< json_spirit::Object >();
	for (vector<json_spirit::Pair>::const_iterator entry = object.begin(); entry !=  object.end(); ++entry) {
		cout << entry->name_ << endl;
		map<string,  Method >::iterator method = methods_.find(entry->name_);
	    if ( method != methods_.end() ) {
	    	   ( (this->*method->second)(entry->value_) );
	    }

	}
}
void  EpsgConfig::callback(const string& name, const json_spirit::Value& value)
{
	cout <<  "HOURA!EpsgConfig::epsg" << name << endl;
	// here we get an Array of epsg!

	assert (value.type() == json_spirit::array_type);
	json_spirit::Array values = value.get_value<json_spirit::Array>();
	for (unsigned int i = 0; i < values.size(); i++) {
		json_spirit::Object object = values[i].get_value< json_spirit::Object >();
		 for (vector<json_spirit::Pair>::const_iterator entry = object.begin(); entry !=  object.end(); ++entry) {
		 	cout << entry->name_ << endl;
		 	Epsg* epsg = new Epsg(entry->name_);
		 	epsg->set( entry->value_);
		 }
	}

}
/*
static Epsg epsg_4336("EPSG:4326",
		"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
		-180, -90, +180, 90,
		-180, -90, +180, 90,
		20, 20, true
		);

static Epsg cylindrical("cylindrical",
				"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
				-180, -90, +180, 90,
				-180, -90, +180, 90,
				20, 20, true
				);

static Epsg polar_north("polar_north",
				"+proj=stere +lat_0=90 +lat_ts=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 +ellps=WGS84 +datum=WGS84 +units=m +no_defs",
				-180, -20, +180, -20,// line this one is to find the limit of the projection coordinate system.
				-45, -20, 135, -20, // this one to define the default full extend
				2,2, false
				);
static Epsg epsg_32661("EPSG:32661",
				"+proj=stere +lat_0=90 +lat_ts=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 +ellps=WGS84 +datum=WGS84 +units=m +no_defs",
				-180, -20, +180, -20,// line this one is to find the limit of the projection coordinate system.
				-45, -20, 135, -20, // this one to define the default full extend
				2,2, false
				);
static Epsg geos("geos",
				"+proj=goode",
				-160,  0, 160, 0,// line this one is to find the limit of the projection coordinate system.
				-180,  -90, 180, 90, // this one to define the default full extend
				2,2, false
				);
*/
/*!
  \brief Constructor
*/
Proj4Projection::Proj4Projection() 
{
	MagLog::debug() << "Proj4Projection::Proj4Projection needs implementing." <<endl;
	//init();
	EpsgConfig config;
	config.init();
}

/*!
  \brief Destructor
*/
Proj4Projection::~Proj4Projection() 
{
	
}

void Proj4Projection::print(ostream& out) const
{
    out << "Proj4Projection[";
    Proj4ProjectionAttributes::print(out);
    out << "]"; 
} 

void Proj4Projection::init()  
{
	static bool done = false;
	if ( done ) return;
	done = true;

	from_ = pj_init_plus("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
	projection_ = Epsg::find(definition_);
	to_    = pj_init_plus(projection_->definition());
	projPJ equiv = pj_latlong_from_proj(to_);




	if ( full_ ) {
		min_longitude_ = projection_->lowerleft_.x_;
		min_latitude_ = projection_->lowerleft_.y_;
		max_longitude_ = projection_->upperright_.x_;
		max_latitude_ = projection_->upperright_.y_;
	}

	GeoPoint ll (min_longitude_, min_latitude_);
	GeoPoint ur (max_longitude_, max_latitude_);
	PaperPoint xyleft = (full_) ? (*this)( projection_->left_) : (*this)(ll);
	PaperPoint xyright = (full_) ? (*this)( projection_->right_) : (*this)(ur);
	PaperPoint xytop = (full_) ? (*this)( projection_->top_) : (*this)(ur);
	PaperPoint xybottom = (full_) ? (*this)( projection_->bottom_) : (*this)(ll);

	min_pcx_ = xyleft.x_;
	max_pcx_ = xyright.x_;
	min_pcy_ = xybottom.y_;
	max_pcy_ = xytop.y_;





}

PaperPoint Proj4Projection::operator()(const UserPoint& point)  const
{
	MagLog::dev() << "Proj4Projection::operator()(...) needs implementing." << endl;
	return Transformation::operator()(point);
}

PaperPoint Proj4Projection::operator()(const GeoPoint& point)  const
{

	if ( pj_is_latlong(to_) )
		return PaperPoint(point.x(), point.y());
	double x = point.x();
	double y = point.y();

	x *= DEG_TO_RAD;
	y *= DEG_TO_RAD;

    pj_transform(from_, to_, 1, 1, &x, &y, NULL);
	return PaperPoint(x, y);
	               
}

PaperPoint Proj4Projection::operator()(const PaperPoint& point)  const
{
	MagLog::dev() << "Proj4Projection::operator()(...) needs implementing." << endl;
	return Transformation::operator()(point);
}

void Proj4Projection::setNewPCBox(double minx, double miny, double maxx, double maxy)
{
	   PaperPoint p1(minx, miny);
	   PaperPoint p2(maxx, maxy);
	   GeoPoint   ll, ur;

	   revert(p1, ll);
	   revert(p2, ur);

	   min_longitude_ =ll.x();
	   max_longitude_ = ur.x();
	   min_latitude_ = ll.y();
	   max_latitude_ = ur.y();
	min_pcx_ = minx;
	max_pcx_ = maxx;
	min_pcy_ = miny;
	max_pcy_ = maxy;
}

void Proj4Projection::revert(const PaperPoint& xy, GeoPoint& point)  const
{
	if ( pj_is_latlong(to_) ) {
		point = GeoPoint(xy.x(), xy.y());
		return;
	}

	double x = xy.x();
	double y = xy.y();
		

	    pj_transform(to_, from_, 1, 1, &x, &y, NULL );
	    

		x *= RAD_TO_DEG;
		y *= RAD_TO_DEG;
	

		point = GeoPoint(x, y);
}

void Proj4Projection::revert(const PaperPoint& xy, UserPoint& point)  const
{
	MagLog::dev() << "Proj4Projection::revert(...) needs implementing." << endl;
	Transformation::revert(xy, point);
}

bool Proj4Projection::needShiftedCoastlines()  const
{
	// Will need w new parameter to know!
	return false;
}

void Proj4Projection::aspectRatio(double& width, double& height)  
{
	MagLog::dev() << "Proj4Projection::aspectRatio(...) needs implementing." << endl;
	Transformation::aspectRatio(width, height);
}

static double gridMinLon_;
static double gridMinLat_;
static double gridMaxLon_;
static double gridMaxLat_;

void Proj4Projection::boundingBox(double& xmin, double& ymin, double& xmax, double& ymax)  const
{

	// Here we have to find the max Bounding box !
	    vector<pair<double, double> > geo;
		vector<pair<double, double> > xy;

		double xpcmax =  max_pcx_;
		double xpcmin =  min_pcx_;
		double ypcmax =  max_pcy_;
		double ypcmin =  min_pcy_;


		const double xs = (xpcmax- xpcmin)/99.;
		const double ys = (ypcmax- ypcmin)/99.;
		// Walk along the boundary...
		double x,y;


		x = xpcmin;
		for (int i = 0; i < 100; i++) {
			x = xpcmin +(i*xs);
			for (int j = 0; j < 100; j++) {
				y = ypcmin +(j*ys);
				xy.push_back(make_pair(x, y));
			}
		}

		revert(xy, geo);
		xmin=DBL_MAX;
		xmax=DBL_MIN;
		ymin=DBL_MAX;
		ymax=DBL_MIN;

		for (vector<pair<double, double> >::iterator point = geo.begin(); point != geo.end(); ++point) {

			if (point->first == -1000 || point->second == -1000)
				continue;
			if ( xmin > point->first) xmin = point->first;
			if ( xmax < point->first) xmax = point->first;
			if ( ymin > point->second) ymin = point->second;
			if ( ymax < point->second) ymax = point->second;
		}
		if (xmax-xmin > 350 )  { // make it global! {
			xmin-=5;
			xmax=xmin+370;
		}
		gridMinLon_ = xmin;
		gridMinLat_ = ymin;
		gridMaxLon_ = xmax;
		gridMaxLat_ = ymax;


		MagLog::dev() << "Proj4Projection::boundingBox-->[" << gridMinLon_ << ", " << gridMinLat_ << "] -> [" << gridMaxLon_ << ", " << gridMaxLat_ << "]" << endl;
}

double Proj4Projection::getMinX()  const
{
	return gridMinLon_;
}

double Proj4Projection::getMinY()  const
{
	
	return gridMinLat_;
}

double Proj4Projection::getMaxX()  const
{
	return gridMaxLon_;
}

double Proj4Projection::getMaxY()  const
{
	return gridMaxLat_;
}

void Proj4Projection::setMinX(double x)  
{
	min_longitude_ = x;
}

void Proj4Projection::setMinY(double y)  
{
	min_latitude_ = y;
}

void Proj4Projection::setMaxX(double x)  
{
	
	max_longitude_ = x;
}

void Proj4Projection::setMaxY(double y)  
{

	max_latitude_ = y;
}

double Proj4Projection::getMinPCX()  const
{
	return min_pcx_;
}

double Proj4Projection::getMinPCY()  const
{
	return min_pcy_;
}

double Proj4Projection::getMaxPCX()  const
{
	return max_pcx_;
}

double Proj4Projection::getMaxPCY()  const
{
	return max_pcy_;
}

void Proj4Projection::gridLongitudes(const GridPlotting& grid)  const
{


	vector<double> longitudes = grid.longitudes();


		const double step = projection_->steplat_;
		longitudes.push_back(180);
		const vector<double>::const_iterator lon_end =longitudes.end();

		for (vector<double>::const_iterator lon = longitudes.begin(); lon != lon_end; ++lon)
		{
			cout << "plot lon->" << *lon << endl;
			Polyline poly;
			poly.setAntiAliasing(false);

			for (double lat = gridMinLat_; (lat == gridMaxLat_ || lat < gridMaxLat_ + step); lat += step)
			{
					poly.push_back((*this)(GeoPoint(*lon,lat)));
			}
			grid.add(poly);
		}
}

void Proj4Projection::gridLatitudes(const GridPlotting& grid)  const
{
	const vector<double>& latitudes = grid.latitudes();

	const double step = projection_->steplon_;
	const vector<double>::const_iterator lat_end = latitudes.end();
	for(vector<double>::const_iterator lat = latitudes.begin(); lat != lat_end; ++lat)
	{

		Polyline poly;
		poly.setAntiAliasing(false);
		for (double lon = gridMinLon_; lon <= gridMaxLon_ + step; lon += step)
		{
			poly.push_back((*this)(GeoPoint(lon,*lat)));
		}
		grid.add(poly);
	}
}

void Proj4Projection::labels(const LabelPlotting& label, DrawingVisitor& visitor)  const
{

	if ( projection_->latlonlabel_ ) return;
	const vector<double>& longitudes = label.longitudes();
	const vector<double>& latitudes = label.latitudes();
	for (unsigned int lat = 0; lat < latitudes.size(); lat++ )
		{
		    for (unsigned int lon = 0 ; lon < longitudes.size(); lon++ )
		    {
		   	   GeoPoint point(longitudes[lon],latitudes[lat]);
		   	   PaperPoint xy = (*this)(point);

		   	   if ( !in(xy) ) continue;

		   	   Text *text = new Text();
		   	   text->setText(point.writeLatitude());
		       text->push_back(xy);
		       text->setBlanking(true);
		   	   label.add(text);
		    }
		}
}


void Proj4Projection::labels(const LabelPlotting& label, LeftAxisVisitor& visitor)  const
{

	if ( projection_->latlonlabel_ ) {
		const vector<double>& latitudes = label.latitudes();

		for (unsigned int lat = 0; lat < latitudes.size(); lat++ )
		{
			double lon = max_longitude_ - ((max_longitude_-min_longitude_)*.1);
			GeoPoint point(lon,latitudes[lat]);
			PaperPoint xy = (*this)(point);
			if ( !in(xy) ) continue;
			Text *text = new Text();
			text->setText(point.writeLatitude());
			text->push_back(xy);
			text->setJustification(MRIGHT);
			text->setVerticalAlign(MHALF);
			text->setBlanking(true);
			label.add(text);
		}
	}
	else {
		double x = max_pcx_ - ((max_pcx_-min_pcx_)*.1);
		// we calculate the intersection of the longitudes with the left side
		verticalLabels(label, min_pcx_, x, MRIGHT);

	}
}

void Proj4Projection::labels(const LabelPlotting& label, RightAxisVisitor& visitor)  const
{

	if ( projection_->latlonlabel_ ) {
		const vector<double>& latitudes = label.latitudes();
		for (unsigned int lat = 0; lat < latitudes.size(); lat++ )
		{
			double lon = min_longitude_ + ((max_longitude_-min_longitude_)*.1);
			GeoPoint point(lon,latitudes[lat]);
			PaperPoint xy = (*this)(point);
			if ( !in(xy) ) continue;
			Text *text = new Text();
			text->setText(point.writeLatitude());
			text->push_back(xy);
			text->setJustification(MLEFT);
			text->setVerticalAlign(MHALF);
			text->setBlanking(true);
			label.add(text);
		}
	}
	else {
		// we calculate the intersection of the longitudes with the right side
		double x = min_pcx_ + ((max_pcx_-min_pcx_)*.1);
		verticalLabels(label, max_pcx_, x, MLEFT);

	}
}

void Proj4Projection::labels(const LabelPlotting& label, BottomAxisVisitor& visitor)  const
{

	if ( projection_->latlonlabel_ ) {
		const vector<double>& longitudes = label.longitudes();
		const double lat = min_latitude_ + (max_latitude_-min_latitude_)*.8;
		for (unsigned int lon = 0; lon < longitudes.size(); lon++ )
		{
			GeoPoint point(longitudes[lon],lat);
			PaperPoint xy = (*this)(point);
			if ( !in(xy) ) continue;
			Text *text = new Text();
			text->setText(point.writeLongitude());
			text->push_back(xy);
			text->setJustification(MCENTRE);
			text->setVerticalAlign(MTOP);
			text->setBlanking(true);
			label.add(text);
		}
	}
	else {
			// we calculate the intersection of the longitudes with the right side
		double y = min_pcy_ + ((max_pcy_-min_pcy_)*.8);
		horizontalLabels(label, min_pcy_, y, MTOP);
	}
}

inline double CA(PaperPoint& p1, PaperPoint& p2)
{
    return (p2.x() - p1.x()) ? (p2.y() - p1.y())/(p2.x() - p1.x()) : 0;
}

inline double CB(double a, PaperPoint& p)
{
    return p.y() - a * p.x();
}

inline double CX(double a, double b, double y)
{
    return (a) ? (y - b)/a : 0;
}
inline double CY(double a, double b, double x)
{
    return (a * x) + b;
}
inline bool between(double x, double x1, double x2)
{
	return ( std::min(x1, x2) <= x && x <= std::max(x1, x2) );
}

void Proj4Projection::verticalLabels(const LabelPlotting& label, double x, double pos, Justification justif)  const
{
    const vector<double>& longitudes = label.longitudes();
    for (vector<double>::const_iterator lon = longitudes.begin(); lon != longitudes.end(); ++lon)
    {
        // find the equation of the line using 2 points : lon/-20 -->lon/ +20
    	for ( double lat1 = -90, lat2 = -80; lat2 < 90; lat1+=10, lat2+=10) {
    		GeoPoint geo1(*lon, lat1);
    		GeoPoint geo2(*lon, lat2);
    		PaperPoint xy1 = (*this)(geo1);
    		PaperPoint xy2 = (*this)(geo2);
    		if ( between(x, xy1.x_, xy2.x_) ) {
    			double a = CA(xy1, xy2);
    			double b = CB(a, xy1);
    			PaperPoint xy(x, CY(a, b, x));
    			if ( !in(xy) ) continue;
    			GeoPoint geo;
    	        revert(xy, geo);
    	        xy.x(pos);
    			if ( !same(geo.longitude(),*lon ) ) continue;
    			Text* text = new Text();
    			text->setJustification(justif);
    			text->setVerticalAlign(MHALF);
    			cout << "add Text->" << geo.writeLongitude() << endl;
    		    text->setText(geo.writeLongitude());
    		    text->push_back(xy);
    		    label.add(text);
    		}
    	}
    }

}
void Proj4Projection::horizontalLabels(const LabelPlotting& label, double y, double pos, VerticalAlign align)  const
{
	const vector<double>& longitudes = label.longitudes();
	for (vector<double>::const_iterator lon = longitudes.begin(); lon != longitudes.end(); ++lon) {
	    // find the equation of the line using 2 points : lon/-20 -->lon/ +20
		for ( double lat1 = -90, lat2 = -80; lat2 < 90; lat1+=10, lat2+=10) {
			GeoPoint geo1(*lon, lat1);
			GeoPoint geo2(*lon, lat2);
			PaperPoint xy1 = (*this)(geo1);
			PaperPoint xy2 = (*this)(geo2);
			if ( between(y, xy1.y_, xy2.y_) ) {
				double a = CA(xy1, xy2);
				double b = CB(a, xy1);
				PaperPoint xy(CX(a, b, y), y);
				if ( !in(xy) ) continue;
	        	GeoPoint geo;
	        	revert(xy, geo);
	        	xy.y(pos);
	        	if ( !same(geo.longitude(), *lon ) ) continue;
	        	Text* text = new Text();
	        	text->setJustification(MCENTRE);
	        	text->setVerticalAlign(align);
	        	text->setText(geo.writeLongitude());
	        	text->push_back(xy);
	        	label.add(text);
			}
		}
	}
}

void Proj4Projection::labels(const LabelPlotting& label, TopAxisVisitor& visitor)  const
{
	if ( projection_->latlonlabel_ ) {
		const vector<double>& longitudes = label.longitudes();
		const double lat = min_latitude_ + (max_latitude_-min_latitude_)*.2;
		for (unsigned int lon = 0; lon < longitudes.size(); lon++ )
		{
			GeoPoint point(longitudes[lon],lat);
			PaperPoint xy = (*this)(point);
			if ( !in(xy) ) continue;
			Text *text = new Text();
			text->setText(point.writeLongitude());
			text->push_back(xy);
			text->setJustification(MCENTRE);
			text->setVerticalAlign(MBOTTOM);
			text->setBlanking(true);
			label.add(text);
		}
	}
	else {
		// we calculate the intersection of the longitudes with the right side
		double y = min_pcy_ + ((max_pcy_-min_pcy_)*.2);
		horizontalLabels(label, max_pcy_, y, MBOTTOM);
	}
}


void Proj4Projection::revert(const vector<pair<double, double> > & in, vector<pair<double, double> > & out) const
{
	
	out.reserve(in.size());
	for ( vector<pair<double, double> >::const_iterator pt = in.begin();  pt != in.end(); ++pt) {
		  double x = pt->first;
		  double y = pt->second;
		  if ( !pj_is_latlong(to_) ) {
			  if ( pj_transform(to_, from_, 1, 1, &x, &y, NULL ) == 0 ) {
				  // revrt to see!
				  double xr = x;
				  double yr = y;
				  pj_transform(from_, to_, 1, 1, &xr, &yr, NULL );
/*
				  if ( !same(xr, pt->first) ||  !same(yr, pt->second) ) {
					  x = -1000;
					  y = -1000;
				  }


				 else {
*/
					  x *= RAD_TO_DEG;
				      y *= RAD_TO_DEG;
				      if (x < projection_->left_.x_ )
						  x = projection_->left_.x_;
//				  }

			  }
		  }
		  out.push_back(make_pair(x, y));
	}
}
void Proj4Projection::coastSetting(map<string, string>& setting, double abswidth, double absheight) const
{
	// work out the ratios of geographical to paper lengths
	//const double xratio = ( xpcmax_ - xpcmin_ ) / abswidth;
	//const double yratio = ( ypcmax_ - ypcmin_ ) / absheight;

	// choose the smallest (smaller ratio means more detail required)
	const double ratio = 10;

	std::string resol = "110m";
	if ( ratio < 100000 )  // highest resolution
	{
		resol = "10m";
	}
	else if ( ratio < 300000)   // medium resolution
	{
		resol = "50m";
	}
	resol = "110m";
	setting["resolution"]      = resol;
	setting["lakes"]      = resol + "/" + resol + "_lakes";
	setting["land"]       = resol + "/" + resol + "_land";
	setting["rivers"]     = resol + "/" + resol + "_rivers_lake_centerlines";
	setting["boundaries"] = resol + "/" + resol + "_admin_0_boundary_lines_land";
	setting["administrative_boundaries"] = resol + "/" + resol + "_admin_1_states_provinces_shp";

	MagLog::dev() << "GeoRectangularProjection::coastSetting[" << abswidth << ", " << absheight << "]->" <<  ratio << " resol: "<<resol<< endl;
}
void Proj4Projection::visit(MetaDataVisitor& visitor, double left, double top, double width, double height)
{

	ostringstream java;
	double w = getMaxPCX() - getMinPCX();
	double h = getMaxPCY() - getMinPCY();
	java << "{";
	java << "\"name\" : \"proj4\",";
    java << "\"definition\" : \"" << definition_ <<  "\",";
    java << "\"proj4_definition\" : \"" << projection_->definition_ <<  "\",";
	java << "\"top\" : \"" << top <<  "\",";
	java << "\"left\" : \"" << left <<  "\",";
	java << "\"width\" : \"" << width <<  "\",";
	java << "\"height\" : \"" << height <<  "\",";

	java << "\"pcxmin\" : \"" << getMinPCX() <<  "\",";
	java << "\"pcymin\" : \"" << getMinPCY() <<  "\",";
	java << "\"pcwidth\" : \"" << w <<  "\",";
	java << "\"pcheight\" : \"" << h <<  "\"";

	java << "}";
	visitor.add("projection", java.str());
	ostringstream wf;
	wf << (w/width)<< endl;
	wf << "0\n0\n";
	wf << -(h/height) << endl;
	wf << getMaxPCY() - (h/height)/2<< endl;
	wf <<  getMinPCX() +  (w/width)/ 2<< endl;
	visitor.add("world_file", wf.str());
}
