.FixMapLons <- function(world, fun, thr, shift, fixAnt = FALSE) {
  # world must be a maps map
  # fun must be `>` or `<`
  # thr must be the threshold to use with the comparison function
  # shift must be the shift to apply to the points beyond thr
  # fixAnt is a boolean that tells whether to add some points to the 
  # antarctic continent so that it can be filled properly
  splitByNA <- function(x) {
    ids <- 1 + cumsum(is.na(x))
    not.na <- !is.na(x)
    split(x[not.na], ids[not.na])
  }
  mergeByNA <- function(x) {
    as.vector(unlist(sapply(x, function(y) c(y, NA))))
  }
  intersectThr <- function(a, b, xthr) {
    v <- b - a
    m <- v[2] / v[1]
    n <- a[2] - m * a[1]
    ythr <- m * xthr + n
    ythr
  }
  lands_x <- splitByNA(world$x)
  lands_y <- splitByNA(world$y)
  lands <- world$names
  lands <- c(lands, rep("", length(lands_x) - length(lands)))
  if (fixAnt) {
    ant_pos <- which(lands == 'Antarctica')
    lands_x[[ant_pos]][length(lands_x[[ant_pos]])] <- 360
    lands_y[[ant_pos]][length(lands_y[[ant_pos]])] <- -90
    lands_x[[ant_pos]] <- c(0, lands_x[[ant_pos]], 180, 0)
    lands_y[[ant_pos]] <- c(-90, lands_y[[ant_pos]], -90, -90)
  } 
  lands_to_remove <- c()
  for (land in 1:length(lands)) {
    bad_lons <- which(fun(lands_x[[land]], thr))
    if (length(bad_lons) > 0) {
      new_land_n <- 1
      previous_lon <- NULL
      first_land_lon <- bad_lons[1]
      points_to_remove <- c()
      points_to_add <- c()
      #points_to_add_x <- c()
      #points_to_add_y <- c()
      for (bad_lon in bad_lons) {
        if (!is.null(previous_lon)) {
          if ((bad_lon - previous_lon) > 1) {
            # Add intersection point 1 with the side limit
            new_land_x <- c()
            new_land_y <- c()
            if (first_land_lon > 1) {
              intersect_y <- intersectThr(c(lands_x[[land]][first_land_lon - 1],
                                            lands_y[[land]][first_land_lon - 1]),
                                          c(lands_x[[land]][first_land_lon],
                                            lands_y[[land]][first_land_lon]),
                                          thr)
              new_land_x <- c(new_land_x, thr + shift)
              new_land_y <- c(new_land_y, intersect_y)
              #points_to_add <- c(points_to_add, 
              #                   first_land_lon - length(points_to_remove) + length(points_to_add))
              #points_to_add_x <- c(points_to_add_x, thr)
              #points_to_add_y <- c(points_to_add_y, intersect_y)
            }
            # Move the points that are beyond the limit
            new_land_x <- c(new_land_x, lands_x[[land]][first_land_lon:previous_lon] + shift)
            new_land_y <- c(new_land_y, lands_y[[land]][first_land_lon:previous_lon])
            points_to_remove <- c(points_to_remove, first_land_lon:previous_lon)
            # Add intersection point 2 with the side limit
            intersect_y <- intersectThr(c(lands_x[[land]][previous_lon],
                                          lands_y[[land]][previous_lon]),
                                        c(lands_x[[land]][bad_lon],
                                          lands_y[[land]][bad_lon]),
                                          thr)
            new_land_x <- c(new_land_x, thr + shift)
            new_land_y <- c(new_land_y, intersect_y)
            #points_to_add <- c(points_to_add, 
            #                   (bad_lon - length(points_to_remove) + length(points_to_add)):
            #                   (bad_lon - length(points_to_remove) + length(points_to_add) + 1))
            #points_to_add_x <- c(points_to_add_x, NA, thr)
            #points_to_add_y <- c(points_to_add_y, NA, intersect_y)
            # Move the points that are beyond the limit (create a new land with them)
            lands_x <- c(lands_x, list(new_land_x))
            lands_y <- c(lands_y, list(new_land_y))
            lands <- c(lands, paste0(lands[land], '_', new_land_n))
            new_land_n <- new_land_n + 1
            first_land_lon <- bad_lon
          }
        }
        previous_lon <- bad_lon
      }
      # Add intersection point 1 with the side limit
      new_land_x <- c()
      new_land_y <- c()
      if (first_land_lon > 1) {
        intersect_y <- intersectThr(c(lands_x[[land]][first_land_lon - 1],
                                      lands_y[[land]][first_land_lon - 1]),
                                    c(lands_x[[land]][first_land_lon],
                                      lands_y[[land]][first_land_lon]),
                                      thr)
        new_land_x <- c(new_land_x, thr + shift)
        new_land_y <- c(new_land_y, intersect_y)
        #points_to_add <- c(points_to_add, 
        #                   first_land_lon - length(points_to_remove) + length(points_to_add))
        #points_to_add_x <- c(points_to_add_x, thr)
        #points_to_add_y <- c(points_to_add_y, intersect_y)
      }
      # Move the points that are beyond the limit
      new_land_x <- c(new_land_x, lands_x[[land]][first_land_lon:previous_lon] + shift)
      new_land_y <- c(new_land_y, lands_y[[land]][first_land_lon:previous_lon])
      points_to_remove <- c(points_to_remove, first_land_lon:previous_lon)
      # Add intersection point 2 with the side limit
      if (previous_lon < length(lands_x[[land]])) {
        intersect_y <- intersectThr(c(lands_x[[land]][previous_lon],
                                      lands_y[[land]][previous_lon]),
                                    c(lands_x[[land]][previous_lon + 1],
                                      lands_y[[land]][previous_lon + 1]),
                                      thr)
        new_land_x <- c(new_land_x, thr + shift)
        new_land_y <- c(new_land_y, intersect_y)
        #points_to_add <- c(points_to_add, 
        #                   (previous_lon - length(points_to_remove) + length(points_to_add)):
        #                   (previous_lon - length(points_to_remove) + length(points_to_add) + 1))
        #points_to_add_x <- c(points_to_add_x, NA, thr)
        #points_to_add_y <- c(points_to_add_y, NA, intersect_y)
      }
      # Move the points that are beyond the limit (create a new land with them)
      lands_x <- c(lands_x, list(new_land_x))
      lands_y <- c(lands_y, list(new_land_y))
      lands <- c(lands, paste0(lands[land], '_', new_land_n))

      # Finally remove the points accumulated
      lands_x[[land]] <- lands_x[[land]][-points_to_remove]
      lands_y[[land]] <- lands_y[[land]][-points_to_remove]
      if (length(lands_x[[land]]) == 0) {
        lands_to_remove <- c(lands_to_remove, land)
      #} else {
      #  # And add the points accumulated
      #  new_points_x <- new_points_y <- vector('numeric', length(lands_x[[land]]) + length(points_to_add))
      #  new_points_x[points_to_add] <- points_to_add_x
      #  new_points_x[which(!(1:length(new_points_x) %in% points_to_add))] <- lands_x[[land]]
      #  lands_x[[land]] <- new_points_x
      #  new_points_y[points_to_add] <- points_to_add_y
      #  new_points_y[which(!(1:length(new_points_y) %in% points_to_add))] <- lands_y[[land]]
      #  lands_y[[land]] <- new_points_y
      }
    }
  }
  if (length(lands_to_remove) > 0) {
    lands_x <- lands_x[-lands_to_remove]
    lands_y <- lands_y[-lands_to_remove]
    lands <- lands[-lands_to_remove]
  }
  world$x <- mergeByNA(lands_x)
  world$y <- mergeByNA(lands_y)
  world$names <- lands
  world$range <- c(min(world$x, na.rm = TRUE), max(world$x, na.rm = TRUE),
                   min(world$y, na.rm = TRUE), max(world$y, na.rm = TRUE))
  world
}

clim.colors <- function(n) {
  colorbar <- colorRampPalette(c("dodgerblue4", "dodgerblue1", "forestgreen", 
                                 "yellowgreen", "white", "white", "yellow", 
                                 "orange", "red", "saddlebrown"))
  colorbar(n)
}

# Thanks to Jealie and nesuribe, as seen in
# http://stackoverflow.com/questions/20977477/how-to-assign-a-specific-color-to-na-in-an-image-plot
image.nan <- function(x, y, z, zlim, col, na.color='gray', 
                      outside.below.color='black', 
                      outside.above.color='white', ...) {
  zstep <- (zlim[2] - zlim[1]) / length(col)  # step in the color palette
  newz.below.outside <- zlim[1] - zstep  # new z for values below zlim
  newz.above.outside <- zlim[2] + zstep  # new z for values above zlim
  newz.na <- zlim[2] + 2 * zstep  # new z for NA

  z[which(z < zlim[1])] <- newz.below.outside  # we affect newz.below.outside
  z[which(z > zlim[2])] <- newz.above.outside  # we affect newz.above.outside
  z[which(is.na(z > zlim[2]))] <- newz.na  # same for newz.na

  zlim[1] <- zlim[1] - zstep  # extend lower limit to include below value
  zlim[2] <- zlim[2] + 2 * zstep  # extend top limit to include the two new 
                                  # values above and na

  # we construct the new color range by including: na.color and na.outside
  col <- c(outside.below.color, col, outside.above.color, na.color)  

  image(x, y, z, zlim = zlim, col = col, ...) # we finally call image(...)
}

PlotEquiMap <- function(var, lon, lat, filled.continents = TRUE, 
                        drawleg = TRUE, greenwich = NULL, 
                        breaks = NULL, cols = NULL, varlim = NULL,
                        colNA = 'white', colSup = 'white', colInf = 'white',
                        map_pars = NULL, ...) {
  if (!(is.numeric(var) && (length(dim(var)) == 2))) {
    stop("Error: parameter 'var' should be a numeric matrix.")
  }
  if (!(is.numeric(lon) && (length(dim(lon)) <= 1)) || any(is.na(lon))) {
    stop("Error: parameter 'lon' should be a numeric vector without NA values.")
  }
  if (!(is.numeric(lat) && (length(dim(lat)) <= 1)) || any(is.na(lat))) {
    stop("Error: parameter 'lat' should be a numeric vector without NA values.")
  }
  if (!all(dim(var) == c(length(lat), length(lon)))) {
    stop("Error: parameters 'lon' and/or 'lat' don't match the size of 'var'.")
  }
  if (length(lon) < 2 || length(lat) < 2) {
    stop("Error: at least a 2 by 2 region should be provided.")
  }

  if (!(is.logical(drawleg) || (drawleg %in% c('E', 'S')))) {
    stop("Error: parameter 'drawleg' should be TRUE, FALSE, 'E' or 'S'.")
  }
  if (!is.null(greenwich)) {
    if (!is.logical(greenwich)) {
      stop("Error: 'greenwich' must be NULL, TRUE or FALSE.")
    }
  }

  if (is.null(varlim)) {
    varlim <- c(min(var, na.rm = TRUE), max(var, na.rm = TRUE))
  } else {
    if (!is.numeric(varlim) || length(varlim) != 2) {
      stop("Error: 'varlim' should be a numeric vector of length 2.")
    }
  }
  if (!is.null(cols)) {
    if (!is.character(cols)) {
      stop("Error: 'cols' must be a character vector.")
    }
  }
  if (is.null(breaks)) {
    if (!is.null(cols)) {
      breaks <- length(cols) + 1
    } else {
      breaks <- 21
    }
  }
  if (is.numeric(breaks)) {
    if (length(breaks) == 1) {
      breaks <- seq(varlim[1], varlim[2], length.out = breaks)
    }
    if (is.null(cols)) {
      cols <- clim.colors(length(breaks) - 1)
    } else if (length(cols) != (length(breaks) - 1)) {
      stop("Error: inconsistent number of 'breaks' and 'cols'.")
    }
  } else {
    stop("Error: 'breaks' must be a numeric vector.")
  }

  default_map_pars <- list(interior = FALSE, fill = filled.continents, 
                           wrap = TRUE, resolution = 0)
  if (!is.null(map_pars)) {
    if (!is.list(map_pars)) {
      stop("Error: 'map' pars should be a list of parameters to the function 'maps::map'.")
    }
    default_map_pars[names(map_pars)] <- map_pars
  }
  map_pars <- default_map_pars

  # Keep the original device parameters
   old_par <- par(no.readonly = TRUE)

  # Reorder the longitudes and latitudes
  lon_reord <- sort(lon, index.return = TRUE)
  lon <- lon_reord$x
  lon_reord <- lon_reord$ix
  first_lon <- lon[1]
  last_lon <- tail(lon, 1)
  if (first_lon > 360 || last_lon > 360 || first_lon < -360 || last_lon < -360) {
    stop("Error: all 'lon' and 'lat' values must be in the range [-360, 360].")
  }
  if (last_lon - first_lon >= 360) {
    stop("Error: max(lon) - min(lon) must be smaller than 360 (i.e. there can't be overlapping values).")
  }
  lat_reord <- sort(lat, index.return = TRUE)
  lat <- lat_reord$x
  lat_reord <- lat_reord$ix
  first_lat <- lat[1]
  last_lat <- tail(lat, 1)
  if (first_lat > 90 || last_lat > 90 || first_lat < -90 || last_lat < -90) {
    stop("Error: all 'lon' and 'lat' values must be in the range [-360, 360].")
  }

  # Work out the best arrangement for legend
  requested_width <- last_lon - first_lon
  requested_height <- last_lat - first_lat
  display_width <- par('fin')[1]
  display_height <- par('fin')[2]
  if (drawleg != FALSE) {
    if ((requested_height/requested_width > display_height/requested_width) && 
        (drawleg != 'S')) {
      layout(matrix(1:2, 1, 2), widths = c(5, 1))
      drawleg <- 'E'
    } else {
      layout(matrix(1:2, 2, 1), heights = c(5, 1))
      drawleg <- 'S'
    }
  }

  # Detect automatically whether the data is across greenwich
  lon_gaps <- lon - c(tail(lon, 1) - 360, lon[-length(lon)])
  greenwich_gap <- lon_gaps[1]
  if (greenwich_gap < min(lon_gaps[-1])) {
    print("Warning: irregular gap detected between last and first longitudes in 'lon'. If 'lon' is irregular, automatic detection of greenwich may be wrong. Use parameter 'greenwich' instead.")
  }
  if (((tail(lon, 1) - head(lon, 1)) / min(lon_gaps[-1])) > length(lon)) {
    across_green <- TRUE  
  } else {
    across_green <- FALSE
  }
  if (is.null(greenwich)) {
    greenwich <- across_green
  } else if (across_green) {
    greenwich <- TRUE
  } 
  # Arrange data in function of if plot across greenwich is requested or not
  # and select the corresponding map.
  if (greenwich) {
    lon_reord <- c(lon_reord[which(lon >= 180)], 
                   lon_reord[which((lon >= -180) & (lon < 180))],
                   lon_reord[which(lon < -180)])
    lon <- c(lon[which(lon >= 180)], lon[which((lon >= -180) & (lon < 180))], 
             lon[which(lon < -180)])
    lon[which(lon >= 180)] <- lon[which(lon >= 180)] - 360
    lon[which(lon < -180)] <- lon[which(lon < -180)] + 360
  } else {
    lon_reord <- c(lon_reord[which(lon >= 0)], lon_reord[which(lon < 0)])
    lon[which(lon < 0)] <- lon[which(lon < 0)] + 360
  }
  map_to_draw <- 'world'

  # Reorder the input matrix
  var[, 1:dim(var)[2]] <- var[, lon_reord]
  var[1:dim(var)[1], ] <- var[lat_reord, ]
  # Plot the variable
  image.nan(as.vector(lon), as.vector(lat), t(var), varlim, cols, 
            colNA, colSup, colInf, xlab = "", ylab = "", ...)
  # With GEOmap
  #data(coastmap, package = 'GEOmap', envir = environment())
  #coastmap$STROKES$col <- rep('black', length(coastmap$STROKES$col))
  #GEOmap::plotGEOmap(coastmap, MAPstyle = 2, add = TRUE)
  # With maps
  #map('world', orientation = c(90, 0, 0), wrap = TRUE)
  # First get the map data
  map_pars[c('database', 'add', 'plot', 'wrap')] <- list(database = map_to_draw, 
                                                         add = FALSE, plot = FALSE,
                                                         wrap = TRUE)
  world <- do.call(map, map_pars)
  # Then amend it as needed
  # If geenwich, if filled TRUE or FALSE in both cases, 
  # points beyond -180 and 180 have to be moved
  if (greenwich) {
    world <- .FixMapLons(world, `>`, 180, -360, filled.continents)
    world <- .FixMapLons(world, `<`, -180, 360, FALSE)
  } else {
    world <- .FixMapLons(world, `>`, 360, -360, filled.continents)
    world <- .FixMapLons(world, `<`, 0, 360, FALSE)
  }

  # Finally plot the map
  map_pars[c('database', 'add', 'plot')] <- list(database = world, 
                                                 add = TRUE, plot = TRUE)
  do.call(map, map_pars)

  # Draw the colorbar
  if (drawleg != FALSE) {
    if (drawleg == 'S') {
      image(1:length(cols), 1, t(t(1:length(cols))), axes = FALSE, col = cols,
            xlab = '', ylab = '')
    }
  }

  # Restore the original device parameters
  par(old_par)
}
