I'd like to crop GeoTiff Raster Files using the two mentioned packages, "rgdal" and "raster". Everything works fine, except that the quality of the resulting output tif is very poor and in greyscale rather than colour. The original data are high quality raster maps from the swiss federal office of Topography, example files can be downloaded here.
This is my code:
## install.packages("rgdal")
## install.packages("raster")
library("rgdal")
library("raster")
tobecroped <- raster("C:/files/krel_1129_2012_254dpi_LZW.tif")
ex <- raster(xmn=648000, xmx=649000, ymn=224000, ymx=225000)
projection(ex) <- proj4string(tobecroped)
output <- "c:/files/output.tif"
crop(x = tobecroped, y = ex, filename = output)
In order to reproduce this example, download the sample data and extract it to the folder "c:/files/". Oddly enough, using the sample data the quality of the croped image is alright, but still greyscale.
I played around using the options "datatype", "format", but didnt get anywhere with that. Can anybody point out a solution? Should I supply more information the the input data?
EDIT:
Josh's example works superb with the sample data 2. Unfortunately, the data I have seems to be older and somewhat different. Can you tell me what option I choose if you read the following GDALinfo:
# packages same as above
OldInFile = "C:/files/krel1111.tif"
dataType(raster(OldInFile)
[1] "INT1U"
GDALinfo(OldInFile)
rows 4800
columns 7000
bands 1
lower left origin.x 672500
lower left origin.y 230000
res.x 2.5
res.y 2.5
ysign -1
oblique.x 0
oblique.y 0
driver GTiff
projection +proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333+k_0=1 +x_0=600000+y_0=200000 +ellps=bessel +units=m+no_defs
file C:/files/krel1111.tif
apparent band summary:
GDType hasNoDataValue NoDataValue blockSize1 blockSize2
1 Byte FALSE 0 1 7000
apparent band statistics:
Bmin Bmax Bmean Bsd
1 0 255 NA NA
Metadata:
AREA_OR_POINT=Area
TIFFTAG_RESOLUTIONUNIT=2 (pixels/inch)
TIFFTAG_XRESOLUTION=254
TIFFTAG_YRESOLUTION=254
Warning message:
statistics not supported by this driver
Edit (2015-03-10):
If one simply wants to crop out a subset of an existing GeoTIFF and save the cropped part to a new *.tif file, using gdalUtils::gdal_translate() may be the most straightforward solution:
library(raster) # For extent(), xmin(), ymax(), et al.
library(gdalUtils) # For gdal_translate()
inFile <- "C:/files/krel_1129_2012_254dpi_LZW.tif"
outFile <- "subset.tif"
ex <- extent(c(686040.1, 689715.9, 238156.3, 241774.2))
gdal_translate(inFile, outFile,
projwin=c(xmin(ex), ymax(ex), xmax(ex), ymin(ex)))
Looks like you need to change two details.
First, the *.tif file you're reading in has three bands, so should be read in using stack(). (Using raster() on it will only read in a single band (the first one, by default) producing a monochromatic or 'greyscale' output).
Second (for reasons mentioned here) writeRaster() will by default write out the values as real numbers (Float64 on my machine). To explicitly tell it you instead want to use bytes, give it the argument datatype='INT1U'.
library("rgdal")
library("raster")
inFile <- "C:/files/krel_1129_2012_254dpi_LZW.tif"
outFile <- "out.tif"
## Have a look at the format of your input file to:
## (1) Learn that it contains three bands (so should be read in as a RasterStack)
## (2) Contains values written as Bytes (so you should write output with datatype='INT1U')
GDALinfo(inFile)
## Read in as three separate layers (red, green, blue)
s <- stack(inFile)
## Crop the RasterStack to the desired extent
ex <- raster(xmn=648000, xmx=649000, ymn=224000, ymx=225000)
projection(ex) <- proj4string(s)
s2 <- crop(s, ex)
## Write it out as a GTiff, using Bytes
writeRaster(s2, outFile, format="GTiff", datatype='INT1U', overwrite=TRUE)
All of which outputs the following tiff file:
Related
I want to create a new raster from 2 tiles. For the date "247" everything works fine. But for the next date "248", the raster I'm getting in return is only the first tile h12v12, is not merging the consecutive one (h13v12).
This is just an example. I've worked with more files and had the same problem with some random files. Still, for some of them, I've got the desired results.
The files:
https://drive.google.com/drive/folders/1FSUAg-H8ePP9jeZjCTatlGFsDsKCq3cN?usp=sharing
#Open files for a day
files <- c("AOD2022248.h12v12.tif", "AOD2022248.h13v12.tif")
files_2 <- c("AOD2022247.h12v12.tif", "AOD2022247.h13v12.tif")
#create stack for each one
test_1 <- stack(files[1])
test_2 <- stack(files[2])
test_3 <- stack(files_2[1])
test_4 <- stack(files_2[2])
The first try was with raster::mosaic. For the 248 file, I've got an error. For the 247 file, it worked.
joint <- mosaic(test_1, test_2, fun=mean, filename = "joint.tif", overwrite=TRUE)
Error in v[cells, i] <- as.vector(getValues(x[[i]])) :
number of items to replace is not a multiple of replacement length
joint2 <- mosaic(test_3, test_4, fun=mean, filename = "joint2.tif", overwrite=TRUE)
I don't get this error, the rasters are not the same extent because they are supposed to be side by side, not on top of each other.
So... the second try was with gdalUtils::mosaic_rasters. Although I didn't get any errors here, when I open the new tif in QGIS the 248 only has the first tile, and the 247 has the two of them.
mosaic_rasters(files, dst_dataset= "files.tif")
[1] "AOD2022248.h12v12.tif" "AOD2022248.h13v12.tif"
NULL
mosaic_rasters(files_2, dst_dataset= "files2.tif")
[1] "AOD2022247.h12v12.tif" "AOD2022247.h13v12.tif"
NULL
When I use verbose = TRUE in mosaic_rasters...
I've got for 247
Input file size is 2400, 12000...10...20...30...40...50...60...70...80...90...100 - done.
and for 248
Input file size is 1200, 12000...10...20...30...40...50...60...70...80...90...100 - done.
I also compared the rasters to see if there is any difference in h12v12 or h13v12 between days but they are the same.
> compareRaster(test_1, test_3)
[1] TRUE
> compareRaster(test_2, test_4)
[1] TRUE
If the files are the same, why mosaic/merge is only working right with some of them?
The raster stacks need to have the same number of layers
nlayers(test_1)
# [1] 4
nlayers(test_2)
# [1] 3
If we add another dummy layer filled with NA, then the mosaic works. I assumed that the missing layer is the fourth, but you may need to figure out which one is missing in your specific case.
test_2a = stack(test_2, init(test_2[[1]], fun=function(x) rep(NA,x)))
joint <- mosaic(test_1, test_2a, fun=mean, filename = "joint.tif", overwrite=TRUE)
Or, we can use package terra instead of raster
library(terra)
test_1 <- rast(files[1])
test_2 <- rast(files[2])
joint <- mosaic(test_1, test_2)
I was assigned the task to clip a raster from .nc file from a .tif file.
edit (from comment):
i want to extract temp. info from the .nc because i need to check the yearly mean temperature of a specific region. to be comparable the comparison has to occur on exactly the same area. The .nc file is larger than the previously checked area so i need to "clip" it to the extent of a .tif I have. The .tif data is in form 0|1 where it is 0 (or the .tif is smaller than the .nc) the .nc data should be "cliped". In the end i want to keep the .nc data but at the extent of the .tif while still retaining its resolution & projection. (.tif and .nc have different projections&pixel sizes)
Now ordinarily that wouldn't be a problem as i could use raster::crop. This doesn't deal with different projections and different pixel size/resolution though. (I still used it to generate an approximation, but it is not precise enough for the final infromation, as can be seen in the code snippet below). The obvious method to generate a more reliable dataset/rasterset would be to first use a method like raster::projectRaster or raster::sp.Transform # adding sp.transform was done in an edit to the original question and homogenize the datasets but this approach takes too much time, as i have to do this for quite a few .nc files.
I was told the best method would be to generate a normalized matrix from the smaller raster "clip_frame" and then just multiply it with the "nc_to_clip" raster. Doing so should prevent any errors through map projections or other factors. This makes a lot of sense to me in theory but I have no idea how to do this in practice. I would be very grateful to any kind of hint/code snippet or any other help.
I have looked at similar problems on StackOverflow (and other sites) like:
convert matrix to raster in R
Convert raster into matrix with R
https://www.researchgate.net/post/Hi_Is_there_a_way_to_multiply_Raster_value_by_Raster_Latitude
As I am not even sure how to frame the question correctly, I might have overlooked an answer to this problem, if so please point me there!
My (working) code so far, just to give you an idea of how I want to approach the topic (here using the crop-function).
#library(ncdf4)
library(raster)
library(rgdal)
library(tidyverse)
nc_list<-list.files(pattern = ".*0.nc$") # list of .nc files containing raster and temperature information
#nc_to_clip <- lapply(nc_list, raster, varname="GST") # read in as raster
nc_to_clip < -raster(ABC.nc, vername="GST)
clip_frame <- raster("XYZ.tif") # read in .tif for further use as frame
mean_temp_from_raster<-function(input_clip_raster, input_clip_frame){ # input_clip_raster= raster to clip, input_clip_frame
r2_coord<-rasterToPoints(input_clip_raster, spatial = TRUE) # step 1 to extract coordinates
map_clip <- crop(input_clip_raster, extent(input_clip_frame)) # use crop to cut the input_clip_raster (this being the function I have to extend on)
temp<-raster::extract(map_clip, r2_coord#coords) # step 2 to extract coordinates
temp_C<-temp*0.01-273.15 # convert kelvin*100 to celsius
temp_C<-na.omit(temp_C)
mean(temp_C)
return_list<-list(map_clip, mean(temp_C))
return(return_list)
}
mean_tempC<-lapply(nc_to_clip, mean_temp_from_raster,clip_frame)
Thanks!
PS:
I don't have much experience working with .nc files and/or RasterLayers in R as I used to work with ArcGIS/Python (arcpy) for problems like this, which is not an option right now.
Perhaps something like this?
library(raster)
nc <- raster(ABC.nc, vername="GST)
clip <- raster("XYZ.tif")
x <- as(extent(clip), "SpatialPolygons")
crs(x) <- crs(clip)
y <- sp::spTransform(x, crs(nc))
clipped <- crop(nc, y)
Given a raster timeseries (satellite images within the same spatial extent each taken on a different date), how can I add the date as name for each layer/band in a (Geo)Tiff? Is it probably underspecified?
Using R as an example:
library(rgdal)
library(raster)
## example data (taken from https://rspatial.org/raster/rs/1-introduction.html#data)
dir.create('/tmp/data', showWarnings = FALSE)
if (!file.exists('/tmp/data/rs/samples.rds')) {
download.file('https://biogeo.ucdavis.edu/data/rspatial/rsdata.zip', dest = '/tmp/data/rsdata.zip')
unzip('/tmp/data/rsdata.zip', exdir='/tmp/data')
}
## just two raster layers
layers <- c("/tmp/data/rs/LC08_044034_20170614_B1.tif", "/tmp/data/rs/LC08_044034_20170614_B2.tif")
lc_stack <- raster::stack(layers)
## set arbitrary timestamps
lc_stack_z <- setZ(lc_stack,c("2019-09-20", "2019-09-21"))
timeseries_rs <- writeRaster(lc_stack_z, "/tmp/data/rs/timeseries-raster.tiff", "GTiff")
The gdalinfo output is as follows (no band name information):
[...]
Band 1 Block=1497x1 Type=Float32, ColorInterp=Gray
Min=0.096 Max=0.735
Minimum=0.096, Maximum=0.735, Mean=nan, StdDev=nan
NoData Value=-3.39999999999999996e+38
Metadata:
STATISTICS_MAXIMUM=0.73462820053101
STATISTICS_MEAN=nan
STATISTICS_MINIMUM=0.096417911350727
STATISTICS_STDDEV=nan
Band 2 Block=1497x1 Type=Float32, ColorInterp=Undefined
Min=0.075 Max=0.718
Minimum=0.075, Maximum=0.718, Mean=nan, StdDev=nan
NoData Value=-3.39999999999999996e+38
Metadata:
STATISTICS_MAXIMUM=0.71775615215302
STATISTICS_MEAN=nan
STATISTICS_MINIMUM=0.074839904904366
STATISTICS_STDDEV=nan
There is a Tiff Tag Library but there I can't find any (semantically appropriate) tag which would serve my need.
Is there a good practice how to add layer/band names to a (Geo)Tiff -- optimally which can be interpreted/read by other Software like QGis.
I have more than 50 raster files (ASCII format) that I need to crop. I already exported the mask from ArcMap in ASCII format as well and loaded it into R. How can I make it work for all rasters in a row and export them with the same name as before (of course in a different folder to not overwrite)?
I know there is a crop function in the raster package, but I never used it so far. I only stacked them for further habitat analysis.
My Code so far:
#### Use only part of area
files2 <- list.files(path="D:/",full.names=TRUE, pattern = "\\.asc$")
files2
# Create a RasterLayer from first file
mask_raster <- raster(files2[1])
# Crop. But how??
crop(x = , y=mask_raster)
writeRaster(...)`
I did not find an easy solution to crop multiple rasters by a raster, but by a shape file. So I just went back to ArcMap and converted the raster into a shapefile. Then in R, crop and mask were the crucial steps. See code below (modified from Mauricio Zambrano-Bigiarini's code). Hope this helps.
# Reading the shapefile (mask to crop later)
Maskshp <- readOGR("mask.shp")
# Getting the spatial extent of the shapefile
e <- extent(Maskshp)
# for loop with (in this case) 60 runs
for (i in 1:60) {
# Reading the raster to crop
files <- list.files(path="...your path",full.names=TRUE, pattern = "\\.asc$")
Env_raster <- raster(files[i])
# Save the filename
filename <- (paste(basename(files[i]), sep=""))
# Crop the raster
Env_raster.crop <- crop(Env_raster, e, snap="out")
# Dummy raster with a spatial extension equal to the cropped raster,
# but full of NA values
crop <- setValues(Env_raster.crop, NA)
# Rasterize the catchment boundaries, with NA outside the catchment boundaries
Maskshp.r <- rasterize(Maskshp, crop)
# Putting NA values in all the raster cells outside the shapefile boundaries
Maskshp.masked <- mask(x=Env_raster.crop, mask=Maskshp.r)
plot(Maskshp.masked)
#Export file to working directory with original name as new name
writeRaster(Maskshp.masked, filename)
}
Currently I'm trying to work with CALIPSO LiDAR data. The files are distributed in HDF4 (checked it with HDFView). The subdataset I'm interested is the Cloud Top Height, which is stored in a simple table (56160 rows, 5 columns):
So far, I tried two things:
A) Converting HDF subdataset to GeoTiff using gdal_translate (gdalUtils package)
# Get a list of sds names
sds <- get_subdatasets('C:\\Users\\Thomas\\Desktop\\NASA Sat Data\\Cloud Layer Data\\CAL_LID_L2_333mCLay-ValStage1-V3-01.2011-08-16T03-02-01ZN.hdf')
# Isolate name of subdataset and convert
name <- sds[25]
filename <- 'Cloud_Top_Height_Test.tif'
gdal_translate(name, dst_dataset = filename)
# Load the Geotiff created into R
r <- raster(filename)
# fix extent, projection and negative values
e <- extent(-80.016243, 64.831413, -179.183868, -1.711731)
r <- setExtent(r, e)
values(r)[values(r) < 0] = 0 # replace -9999 with NA
crs(r) <- '+proj=utm +zone=28 +ellps=WGS84 +datum=WGS84 +units=m +no_defs'
The result was a raster file which was obviously missing any spatial information (dimensions, extend, crs, resolution), that's why I fixed some things afterwards.
This is the result: .
As you can see, the result are some strange lines where it should be square pixels. I suppose I'm missing some statements in the gdal_translate call maybe? Also, all the adjustments afterwards seem a bit stuporous or let's say 'unprofessional'.
B) Converting the table (subdataset) into a data frame (to convert it into a raster in an second step)
A few days ago I came up with the idea to convert the subdataset (which is essentially 'only' a table) to a data frame and convert it in a second step into a raster, where I specify all spatial information 'from scratch' rather than adjusting it afterwards as in my first attempt. Still, I found no commands/packages to do so and I'm still not confident that this is the right way.
What I need in the end: A proper (georeferenced etc) raster (56160 rows, 5 columns) holding the values of the subdataset from the HDF4 file.
My question: How can I extract a HDF4 subdataset and convert it to a raster having the specifications mentioned above?
Any help is greatly appreciated (and since this is my first post at SO: sorry, if this question is missing any forms required in this forum, criticism is also appreciated)
to convert to dataframe:
as.data.frame(r, xy = TRUE)