# load libraries ---- library(shiny) library(shinyMobile) library(leaflet) library(sf) library(dplyr) library(leafpop) library(DT) # load data ---- owners <- readRDS("./data/owners.rds") listings <- readRDS("./data/listings.rds") last_sale_date <- format(attr(owners, "last_sale_date"), "%Y-%m-%d") sbdvn <- sf::st_read("./data/plats/plats.shp") venice_bndry <- readRDS("./data/venice.rds") venice_facts <- readRDS("./data/venice_facts.rds") beaches <- readRDS("./data/beaches.rds") # define ui ---- ui <- f7Page( title = "St. Andrews", ## header ---- tags$head( tags$link(rel = "manifest", href = "manifest.json"), tags$link(rel = "apple-touch-icon", sizes = "180x180", href = "images/apple-touch-icon.png"), tags$meta(name = "apple-mobile-web-app-capable", content = "yes"), tags$meta(name = "apple-mobile-web-app-status-bar-style", content = "default"), tags$style(HTML(" .dataTables_wrapper { color: white; } .dataTables_wrapper table.dataTable thead th, .dataTables_wrapper table.dataTable tbody td { color: white; } .dataTables_wrapper table.dataTable tbody tr.odd { background-color: #333; } .dataTables_wrapper table.dataTable tbody tr.even { background-color: #444; } .dataTables_wrapper .dataTables_paginate .paginate_button { color: white !important; } .dataTables_wrapper .dataTables_paginate .paginate_button.current{ color: black !important; } .dataTables_filter { display: none; /* Hide the search box */ } ")) ), ## options ---- options = list( theme = "ios", dark = TRUE, pullToRefresh = TRUE ), ## layout ---- f7TabLayout( ### panels ---- panels = tagList( f7Panel( id = "panel-left", side = "left", effect = "push", title = "Menu", f7PanelMenu( id = "menu", f7PanelItem( tabName = "About", title = "About", icon = f7Icon("info_circle"), active = TRUE ), f7PanelItem( tabName = "Venice", title = "Venice", icon = f7Icon("map_pin") ), f7PanelItem( tabName = "Beach", title = "Beach", icon = f7Icon("sun_max_fill") ), f7PanelItem( tabName = "Owners", title = "Owners", icon = f7Icon("person_2_fill") ), f7PanelItem( tabName = "Listings", title = "Listings", icon = f7Icon("tag_fill") ), f7PanelItem( tabName = "Resources", title = "Resources", icon = f7Icon("hammer_fill") ) ) ) ), ### navbar ---- navbar = f7Navbar( title = "St. Andrews Park", hairline = TRUE, leftPanel = TRUE ), ### begin tabs ---- f7Tabs( id = "tabs", animated = TRUE, #### about ---- f7Tab( title = "About", tabName = "About", icon = f7Icon("info_circle"), active = TRUE, f7Card( title = "About", divider = "TRUE", tags$img(src = "images/st_andrews.jpg", width = "100%"), "St. Andrews Park is located in Venice, Florida. The condominiums are a mix of single-family homes, villas (2 and 4 units) and multi-unit buildings (8 units). There are 388 separate properties within 14 subdivisions. St. Andrews Park is one of many communities in the Plantation Golf and Country Club. It is should not be confused with the adjacent community St. Andrews East.", footer = tagList( f7Link("More Info", href = "http://www.cpmi.us/standrews-plantation/outside_home.asp") ) ) ), #### venice ---- f7Tab( title = "Venice", tabName = "Venice", icon = f7Icon("map_pin"), active = FALSE, f7Card( title = "Venice", divider = "TRUE", leafletOutput("venice_map") ), f7Card( title = "Facts", divider = "TRUE", DTOutput("venice_facts") ) ), #### beaches ---- f7Tab( title = "Beach", tabName = "Beach", icon = f7Icon("sun_max_fill"), active = TRUE, f7Card( title = "Beaches", divider = "TRUE", leafletOutput("beach_map"), footer = p("Source: Environmental Protection Agency") ), f7Block( h3("Helpful Links:"), f7List( inset = TRUE, dividers = TRUE, strong = TRUE, outline = FALSE, f7ListItem( title = "EPA Beaches", href = "https://www.epa.gov/beaches", external = TRUE ), f7ListItem( title = "Red Tide Forecast", href = "https://habforecast.gcoos.org/", external = TRUE ), f7ListItem( title = "Healthy Beaches Program", href = "https://fdoh.maps.arcgis.com/apps/instant/nearby/index.html?appid=7106a20597de4bff98cc5ebc7f932047&findSource=0&find=1600%2520Harbor%2520Dr%2520S%252C%2520Venice%252C%2520Florida%252C%252034285&sliderDistance=2", external = TRUE ), f7ListItem( title = "MOTE Beach Conditions", href = "https://visitbeaches.org/beach/6/report/53033", external = TRUE ), f7ListItem( title = "National Hurricane Center", href = "https://www.nhc.noaa.gov/", external = TRUE ) ) ) ), #### owners ---- f7Tab( title = "Owners", tabName = "Owners", icon = f7Icon("person_2_fill"), f7Card( title = "Owners:", divider = TRUE, raised = TRUE, footer = paste0("Last sale date: ", last_sale_date), f7List( inset = TRUE, dividers = TRUE, strong = TRUE, outline = FALSE, f7Text( inputId = "name", label = "Last Name:", placeholder = "\"Patel\"" ), f7Text( inputId = "location", label = "Address:", placeholder = "\"123 Chalmers\"" ), f7Select( inputId = "sub_name", label = "Select Subdivision:", choices = c("All", sort(sbdvn$sub_name)) ), tags$br(), f7Button( inputId = "filterButton", label = "Find Owners", icon = "", color = "blue" ) ) ), f7Card( title = "Map:", divider = TRUE, leafletOutput("map") ), f7Card( title = "Table:", divider = TRUE, DTOutput("table") ), f7Card( title = "Download:", divider = TRUE, f7List( inset = TRUE, downloadButton("download_filtered", "Download Filtered"), downloadButton("download_all", "Download All (388)") ) ) ), #### listings ---- f7Tab( title = "Listings", tabName = "Listings", icon = f7Icon("tag_fill"), f7Card( title = "Map:", divider = TRUE, leafletOutput("listings_map") ), f7Card( title = "Active Listings:", divider = TRUE, DTOutput("listings_table") ) ), #### services ---- f7Tab( title = "Resources", tabName = "Resources", icon = f7Icon("hammer_fill"), f7BlockTitle(title = "Services:", size = "medium"), f7Block( f7List( mode = "links", inset = TRUE, outline = TRUE, dividers = TRUE, strong = TRUE, f7Link(label = "City of Venice", href = "https://www.venicegov.com/"), f7Link(label = "Florida Power & Light", href = "https://www.fpl.com/"), f7Link(label = "Sarasota County", href = "https://www.scgov.net/"), f7Link(label = "Property Appraiser", href = "https://www.sc-pa.com/"), f7Link(label = "Open GIS Portal", href = "https://data-sarco.opendata.arcgis.com/"), f7Link(label = "Waste & Recycling", href = "https://www.venicegov.com/government/public-works/waste-and-recycling"), f7Link(label = "Condo Regulation", href = "https://condos.myfloridalicense.com/"), f7Link(label = "Property Records Search", href = "https://www.sarasotaclerk.com/records/official-records/search-land-records") ) ), f7BlockTitle(title = "Documents:", size = "medium"), f7Block( f7List( mode = "links", inset = TRUE, outline = TRUE, dividers = TRUE, strong = TRUE, f7Link(label = "St. Andrews Covenants", href = "docs/2000_01_01_st_andrews_covenants.pdf"), f7Link(label = "St. Andrews (unrecorded)", href = "docs/2004_06_23_sap_map.pdf"), f7Link(label = "Patios 2", href = "docs/1997_08_12_patios_2_plat.pdf"), f7Link(label = "Patios 3", href = "docs/1998_11_17_patios_3_plat.pdf"), f7Link(label = "Villas 2", href = "docs/1998_09_14_villas_2_plat.pdf") ) ) ) ### end tabs---- ), ### begin scripts ---- tags$script( HTML( " $(document).on('click', '#pdfLink', function(event) { event.preventDefault(); window.open($(this).attr('href'), '_blank'); }); $(document).on('click', '#download_filtered, #download_all', function(event) { event.preventDefault(); window.open($(this).attr('href'), '_self'); }); " ) ) ### end scripts ---- ) ) # end ui ---- # define server ---- server <- function(input, output) { # update tabs depending on side panel observeEvent(input$menu, { updateF7Tabs(id = "tabs", selected = input$menu) }) filteredSbdvn <- reactive({ if (is.null(input$sub_name) || input$sub_name == "All") { return(sbdvn$max_sub_id) } else { return( sbdvn %>% filter(sub_name == input$sub_name) %>% pull(max_sub_id) ) } }) filteredOwners <- reactiveVal(owners) observeEvent(input$filterButton, { filtered_owners <- owners %>% filter(subdivision %in% filteredSbdvn()) %>% filter( grepl(input$name, owner_1, ignore.case = TRUE) | grepl(input$name, owner_2, ignore.case = TRUE) ) %>% filter(grepl(input$location, location, ignore.case = TRUE)) filteredOwners(filtered_owners) }) mean_lat <- reactive({ filteredOwners() %>% st_coordinates() %>% .[, "Y"] %>% mean() }) mean_lng <- reactive({ filteredOwners() %>% st_coordinates() %>% .[, "X"] %>% mean() }) output$map <- renderLeaflet({ if (!is.null(filteredOwners()) && nrow(filteredOwners()) > 0) { leaflet() %>% addProviderTiles("CartoDB.Voyager") %>% addPolygons( data = sbdvn, color = "red", weight = 2, opacity = 0.5, fillOpacity = 0.2, label = ~sub_name, group = "Subdivisions" ) %>% addMarkers( data = filteredOwners(), #color = ~ifelse(homestead == 1, "green", "red"), popup = popupTable( filteredOwners(), row.numbers = FALSE, feature.id = FALSE, zcol = c( "label", "owner_1", "owner_2" ) ), group = "Owners" ) %>% addLayersControl( overlayGroups = c("Subdivisions", "Owners"), options = layersControlOptions(collapsed = FALSE) ) %>% setView(lng = mean_lng(), lat = mean_lat(), zoom = 16) } else { leaflet() %>% addProviderTiles("CartoDB.Voyager") %>% addPolygons( data = sbdvn, color = "red", weight = 2, opacity = 0.5, fillOpacity = 0.2, label = ~sub_name, group = "Subdivisions" ) %>% setView(lng = -82.362253, lat = 27.076199, zoom = 16) } }) output$table <- renderDT({ my_table <- filteredOwners() %>% st_drop_geometry() %>% select(label, owner_1, owner_2, homestead) datatable(my_table, colnames = c("Address", "Owner 1", "Owner 2", "Homestead"), rownames = FALSE, options = list( pageLength = 10, scrollX = TRUE, searching = FALSE, lengthMenu = c(5, 10, 25, 50), dom = 'tpi' ) ) }) prep_mailing <- function(data) { data |> sf::st_drop_geometry() |> dplyr::select(owner_1, owner_2, mailing_address_1, mailing_address_2, mailing_city, mailing_state, mailing_zip_code) |> dplyr::mutate( owner_2 = ifelse(is.na(owner_2), "", owner_2), mailing_address_2 = ifelse(is.na(mailing_address_2), "", mailing_address_2) ) |> dplyr::rename( address_1 = mailing_address_1, address_2 = mailing_address_2, city = mailing_city, state = mailing_state, zip = mailing_zip_code ) } output$download_filtered <- downloadHandler( filename = "st_andrews_owners_filtered.csv", content = function(file) { write.csv(prep_mailing(filteredOwners()), file, row.names = FALSE) } ) output$download_all <- downloadHandler( filename = "st_andrews_owners_all.csv", content = function(file) { write.csv(prep_mailing(owners), file, row.names = FALSE) } ) # listings map ---- output$listings_map <- renderLeaflet({ leaflet() |> addProviderTiles("CartoDB.Voyager") |> addPolygons( data = sbdvn, color = "red", weight = 2, opacity = 0.5, fillOpacity = 0.2, label = ~sub_name ) |> addMarkers( data = listings, lat = ~latitude, lng = ~longitude, popup = ~paste0( "", address, "
", "Price: ", scales::dollar(price), "
", "Sq Ft: ", sqft, "
", "$/Sq Ft: ", scales::dollar(price_per_sqft) ), clusterOptions = markerClusterOptions() ) |> setView(lng = -82.362253, lat = 27.076199, zoom = 16) }) # listings table ---- output$listings_table <- renderDT({ datatable( listings |> select(listed_date, address, sqft, price, price_per_sqft) |> arrange(price_per_sqft) |> mutate( price = scales::dollar(price), price_per_sqft = scales::dollar(price_per_sqft) ), colnames = c("Listed", "Address", "Sq Ft", "Price", "$/Sq Ft"), rownames = FALSE, options = list( pageLength = 25, searching = FALSE, dom = 't' ) ) }) # venice map ---- output$venice_map <- renderLeaflet({ leaflet() %>% addProviderTiles("CartoDB.Voyager") %>% setView(lng = -82.4313, lat = 27.1059, zoom = 12) %>% addPolygons( data = venice_bndry, color = "red", weight = 2, opacity = 0.5, fillOpacity = 0.2 ) }) # venice facts ---- output$venice_facts <- renderDT({ datatable( venice_facts, rownames = FALSE, options = list( pageLength = 10, scrollX = TRUE, searching = FALSE, lengthMenu = c(5, 10, 25, 50), dom = 'tpi' ) ) }) # beach map ---- output$beach_map <- renderLeaflet({ leaflet() %>% addProviderTiles("CartoDB.Voyager") %>% setView(lng = -82.4603, lat = 27.0999, zoom = 12) %>% addMarkers( data = beaches, lat = ~lat, lng = ~lng, popup = ~beach_name ) }) } # end server ---- # Run the app shinyApp(ui, server)