rmapsgeospatialggmaprworldmap

geographical/geospatial map with maps() - R


I have been through a lot of questions and documentation, and since you need to bill to use ggmaps() (because of google cloud services) I started looking for an alternative. I found maps(), and I'm trying to adapt this solution:

data %>%
    rename(x = longitud, y = latitud) %>%
    ggplot() +
        geom_polygon(aes(x = long, y = lat), data = map_data("world")) +
        geom_point(aes(x = x, y = y))

enter image description here

However, I'm getting into a few problems:

This is the data:

data <- structure(list(latitud = c(-30.6833000183105, -41.4000015258789, 
-43.8189010620117, -34.2731018066406, -47.0666999816895, -40.3166999816895, 
-43.4491996765137, -35.7543983459473, -47.1413993835449, -36.6260986328125, 
-54.0410995483398, -37.2118988037109, -33.3086013793945, -37.2792015075684, 
-35.4524993896484, -36.5856018066406, -18.5832996368408, -18.2325000762939, 
-36.4668998718262, -44.75, -44.6591987609863, -44.5936012268066, 
-28.4647006988525, -28.6996994018555, -28.5118999481201, -28.6718997955322, 
-28.7306003570557, -30.5902996063232, -30.6667003631592, -35.1730995178223, 
-48.1591987609863, -48.377498626709, -45.4000015258789, -45.7832984924316, 
-29.94580078125, -38.8652992248535, -30.4386005401611, -31.6646995544434, 
-51.2000007629395, -51.3328018188477, -51.25, -45.5666999816895, 
-45.551700592041, -45.8372001647949, -39.0144004821777, -28.9414005279541, 
-28.7502994537354, -38.6081008911133, -34.9844017028809, -32.8403015136719, 
-29.9953002929688, -18.3999996185303, -35.9000015258789, -35.6169013977051, 
-35.9085998535156, -35.8166999816895, -33.7346992492676, -45.38330078125, 
-35.4068984985352, -32.7571983337402, -32.8502998352051, -33.5938987731934, 
-36.8386001586914, -33.4961013793945, -20.1119003295898, -27.8043994903564, 
-37.7332992553711, -30.9986000061035, -30.8006000518799, -21.9368991851807, 
-22.3652992248535, -22.273099899292, -22.0277996063232, -21.9755992889404, 
-22.289400100708, -22.2791996002197, -38.4303016662598, -38.6866989135742, 
-45.4057998657227, -38.7799987792969, -37.5503005981445, -37.6018981933594, 
-37.8997001647949, -38.0368995666504, -37.9897003173828, -37.7047004699707, 
-37.7963981628418, -37.7092018127441, -31.5835990905762, -27.3635997772217, 
-27.3194007873535, -29.8931007385254, -30.9242000579834, -21.4246997833252, 
-36.5703010559082, -38.2008018493652, -38.0661010742188, -38.4333000183105, 
-31.7422008514404, -31.6881008148193, -31.8117008209229, -31.7714004516602, 
-27.86669921875, -27.5160999298096, -27.9747009277344, -30.7047004699707, 
-36.8499984741211, -36.6500015258789, -36.86669921875, -35.3736000061035, 
-40.5167007446289, -33.4782981872559, -33.198299407959, -36.0499992370605, 
-35.9667015075684, -36.2332992553711, -34.4921989440918, -34.6581001281738, 
-32.8166999816895, -47.3499984741211, -47.5, -29.9811000823975, 
-32.4413986206055, -22.3922004699707, -22.3430995941162, -21.7124996185303, 
-22.4582996368408, -22.4419002532959, -22.4468994140625, -22.5060997009277, 
-33.7219009399414, -33.6613998413086, -35.5574989318848), longitud = c(-71.0500030517578, 
-73.2166976928711, -72.38330078125, -71.371696472168, -72.8000030517578, 
-72.9666976928711, -72.1074981689453, -71.0864028930664, -72.7257995605469, 
-72.4891967773438, -68.7975006103516, -72.3242034912109, -70.3572006225586, 
-71.9847030639648, -71.7332992553711, -71.5255966186523, -69.0466995239258, 
-69.331901550293, -72.6911010742188, -72.7166976928711, -71.8082962036133, 
-71.5477981567383, -71.1782989501953, -70.5500030517578, -71.0064010620117, 
-70.6464004516602, -70.5066986083984, -71.1714019775391, -71.5333023071289, 
-71.0911026000977, -73.0888977050781, -72.9589004516602, -72.5999984741211, 
-72.61669921875, -70.5327987670898, -71.7335968017578, -71.002197265625, 
-71.2546997070312, -72.9332962036133, -73.1091995239258, -72.5167007446289, 
-72.0832977294922, -72.0680999755859, -71.7769012451172, -73.0828018188477, 
-70.2481002807617, -70.4828033447266, -72.8478012084961, -72.0100021362305, 
-71.0255966186523, -70.5867004394531, -70.3000030517578, -71.5167007446289, 
-71.7677993774414, -71.2981033325195, -71.8332977294922, -70.3007965087891, 
-72.4666976928711, -72.2082977294922, -70.736701965332, -70.5093994140625, 
-70.3792037963867, -73.061897277832, -70.8167037963867, -68.8407974243164, 
-70.1268997192383, -72.61669921875, -71.0899963378906, -70.9697036743164, 
-68.5330963134766, -68.6418991088867, -68.1438980102539, -68.6207962036133, 
-68.6074981689453, -68.3447036743164, -68.2427978515625, -72.0105972290039, 
-72.502799987793, -72.6231002807617, -72.9468994140625, -72.5903015136719, 
-72.2782974243164, -71.6239013671875, -71.4781036376953, -71.5199966430664, 
-71.7683029174805, -71.6988983154297, -71.823600769043, -71.4606018066406, 
-70.3392028808594, -70.8380966186523, -71.2514038085938, -70.7731018066406, 
-70.053596496582, -71.5547027587891, -71.2988967895508, -71.3497009277344, 
-71.2332992553711, -71.1492004394531, -71.2658004760742, -70.9302978515625, 
-71.0639038085938, -70.0667037963867, -70.2647018432617, -69.997802734375, 
-70.9244003295898, -72.38330078125, -72.4499969482422, -72.3332977294922, 
-71.8292007446289, -73.2833023071289, -70.7172012329102, -70.8955993652344, 
-72.0832977294922, -72.0167007446289, -72, -71.3731002807617, 
-71.3019027709961, -71, -72.8499984741211, -72.9749984741211, 
-70.8981018066406, -71.3139038085938, -69.5299987792969, -69.5650024414062, 
-69.5167007446289, -68.7363967895508, -68.8886032104492, -68.8775024414062, 
-68.988899230957, -71.5550003051758, -71.3371963500977, -71.7067031860352
)), row.names = c(1L, 136L, 262L, 395L, 507L, 605L, 701L, 789L, 
868L, 996L, 1094L, 1124L, 1172L, 1218L, 61387L, 61546L, 75009L, 
87052L, 99246L, 110237L, 115091L, 125346L, 135758L, 135819L, 
144524L, 154009L, 172251L, 185024L, 192338L, 210797L, 228781L, 
228893L, 238299L, 244626L, 253673L, 274263L, 285367L, 304757L, 
316768L, 328069L, 336044L, 346167L, 351691L, 363302L, 375494L, 
385229L, 402720L, 422016L, 440373L, 451547L, 462674L, 483188L, 
491968L, 496483L, 511332L, 530494L, 546443L, 564800L, 575215L, 
586462L, 602135L, 622841L, 642834L, 657640L, 677273L, 688216L, 
706550L, 724524L, 731829L, 748442L, 748489L, 754030L, 763570L, 
776729L, 785860L, 799355L, 812606L, 832675L, 853030L, 860670L, 
878448L, 889066L, 889167L, 889273L, 889372L, 889466L, 889499L, 
889524L, 913996L, 929594L, 935459L, 953842L, 963352L, 983829L, 
991810L, 1005230L, 1005341L, 1011503L, 1022492L, 1029507L, 1047978L, 
1063655L, 1073799L, 1073936L, 1086040L, 1106251L, 1126146L, 1134776L, 
1154269L, 1170495L, 1181431L, 1192018L, 1197439L, 1212431L, 1231028L, 
1247598L, 1264197L, 1264302L, 1271900L, 1279499L, 1279618L, 1290282L, 
1309415L, 1320521L, 1320606L, 1320753L, 1320827L, 1337638L, 1344817L, 
1355030L, 1368899L, 1381979L, 1393175L), class = "data.frame")

Solution

  • The following code uses the simple features (sf) library to show a map of Chile overlaid with the provided datapoints. The bounding box is set in the parameters to st_crop and can be adjusted as needed without distorting the map. The code uses the Admin 0 - Countries shape file, which is in the public domain and free to use.

    library(sf)
    library(ggplot2)
    library(dplyr);
    library(magrittr);
    
    # download world shapefile from
    # https://www.naturalearthdata.com/downloads/
    #        50m-cultural-vectors/50m-admin-0-countries-2/
    # and extract zip file
    
    world <- st_read(
        # change below line to path of extracted shape file
        'c:/path/to/ne_50m_admin_0_countries.shp' 
      );
    
    world %<>% mutate(active = NAME_EN == 'Chile'); # used to highlight Chile
    
    # convert the dataframe to a sf geometry object
    dsf <- data %>% 
        rowwise %>% 
        mutate(geometry = list(st_point(c(longitud, latitud)))) %>%
        st_as_sf(crs=st_crs(world));
    
    # plot the map
    world %>% st_crop(xmin=-90, xmax=-30, ymin=-60, ymax=-10) %>%
        ggplot() + 
        geom_sf(aes(fill=active), show.legend=F) + # world map with Chile highlighted
        geom_sf(data=dsf, color='#000000') + # point overlay
        scale_fill_manual(values=c('#aaaa66', '#ffffcc')) + # country colors
        scale_x_continuous(expand=c(0,0)) +
        scale_y_continuous(expand=c(0,0)) +
        theme_void() + # remove axis labels and gridlines
        theme(panel.background=element_rect(fill='lightblue'))
    

    The output is shown below. Note that the map does not distort regions that are cropped.

    R output


    Additional explanation

    The sf package provides support for simple feature (sf) geometries. Simple features provide tools for working with geometries such as polygons and points. There is a cheat sheet here that provides a good overview.

    Plotting the base map

    As of release 3.0, ggplot2 provides native support for visualizing simple feature geometries. This allows for us to write:

    world <- st_read(
        # change below line to path of extracted shape file
        'c:/path/to/ne_50m_admin_0_countries.shp' 
      ); 
    
     ggplot(world) + geom_sf()
    

    plot result - map of world

    Simple feature objects are generally stored in data frames that include a column describing the geometry. This allows us to show a map of Chile like so:

     ggplot(world %>% filter(NAME_EN == 'Chile')) + geom_sf()
    

    Or a map with Chile highlighted:

    # create new geometry of world map
    # cropped to (10°S, 60°S) and (90°W, 30°W)
    chileregion <- world %>% st_crop(
        xmin=-90, 
        xmax=-30, 
        ymin=-60, 
        ymax=-10)
    
    # show region with Chile highlighted
    ggplot(chileregion %>% 
        mutate(is.chile = factor(NAME_EN == 'Chile'))) + 
        geom_sf(aes(fill=is.chile), show.legend = F) +
        scale_fill_manual(values=c('gray', 'red'))
    

    base map

    Plotting the points

    We are given a data frame with two columns, latitude and longitude.

    To convert to a simple feature, we first use st_point to create a new point with the given coordinate values:

    dsf <- data %>% #begin with data
      rowwise %>%  # dplyr::rowwise applies mutation to each row individually
      # create a geometry column that provides the point as a geometry
      mutate(geometry = list(st_point(c(longitud, latitud))))
    

    At this point, dsf is simply a data frame with a geometry column; it is not yet a simple feature object. We can use the function st_as_sf to create a sf object from the data frame. In doing so, we will also need to provide a coordinate reference system (CRS) to allow ggplot to project the coordinates provided onto the map rendering. Here we can provide ESPG 4326 as the CRS, which maps the x and y coordinates directly to lat/long:

      # call st_as_sf to convert data frame to a simple geometry object.
      dsf <- st_as_sf(crs=4326);
    

    Since dsf is now a simple geometry, you can plot as such:

    > ggplot(dsf) + geom_sf()
    

    ggplot output - points

    (Note that you could also simply overlay the points on the base map with geom_point(data=data, aes(x=longitud, y=latitud)) without first converting to an sf object. This will work here because the CRS for the base map is also ESPG 4326, which maps x and y directly to longitude and latitude, respectively. Using geom_point, however, will not work in the general case when a coordinate transformation is applied to the geometry.)

    Overlaying

    With both points now defined as geometries, you can simply overlay:

    ggplot() +
      geom_sf(data=chileregion) +
      geom_sf(data=dsf)
    

    overlaying two geometries

    The final plot in the original answer adds some additional visual aesthetics (e.g., blue background) to produce the final map output.