REST请求GeoWebCache执行切片

需求背景:

接到一个需求,需要把卫星影像tif图层切片,生成类似于金字塔的文件目录,然后供APP端下载,离线加载。地图切片其实也有其他的方案,但是由于该项目的其他地图也是发布在geoserver上的,且geoserver默认集成了GeoWebCache,遂直接使用GeoWebCache来执行切片操作。

题外话:GeoWebCache分为2个版本,一个是独立程序,一个是集成在Geoserver中。

首先,版本如下:

那么在Geoserver中,让GeoWebCache执行切片也有2种方式:

一、直接在Geoserver的管理页面操作

第一步:点击“Tile Layers”菜单,会展示当前可以切片的所有图层。如下图

 第二步:找到需要切片的图层,点击“Seed/Truncate”按钮,会进入到如下页面:

上面的这个页面就是真正执行切片任务的页面了,下面的参数填报很关键,记住以下几点:

  1. 理论上,“Number of tasks to use” 用默认的1个或者多个都行,选多个,同一个切片任务可以并行执行,会更快;
  2. “Type of operation”有三种操作:seed表示执行切片,reseed表示再次执行切片,truncate表示删除切片的成果文件。
  3. “Grid Set”一定要选择你要切的图层对应的坐标系。默认内置了2种坐标系,如果坐标系不符合你的图层,要么先利用QGIS等将图层坐标系转成EPSG:4326,要么在Geoserver首页的“Caching Defaults”页面中添加切片的坐标系。
  4. “Format”:切片的成果格式,png和jpg两种,如果有其他的格式需要,也可以在“Caching Defaults”中配置。
  5. Zoom start和Zoom stop分别表示切片的最小和最大层级,根据图层情况自行设置,拿捏不准的,可以先在Geoserver首页的“Layer Preview”中预览要切片的图层看看,缩放哪些层级合适;
  6. “STYLES”:图层样式;
  7. “Bounding box”:可以不填,空着的话就是取坐标系的边界;

点击“submit”按钮,提交切片任务,页面会刷新,展示任务状态,如下图:

那么问题来了,切好的图在电脑或服务器的哪个位置呢?可以通过访问:http://IP地址:端口/geoserver/gwc

 进行查看,在页面底部可以看到,如下图:

 打开成果目录,如下:

 可以看到文件夹是以切片的图层名称命名的,目录下面就是根据zoom缩放层级分类的文件夹了:

 

二、利用RESTFUL API接口进行操作

如果想让切片更加自动化,刚好GeoWebCache提供了REST API操作。文档地址:Seeding and Truncating — GeoServer 2.24.x User Manual

吐槽下文档写得很有问题,我按照官方提供的示例尝试过很多次提交执行切片任务都无法成功。最后发现既然在Geoserver管理页面提交执行切片任务可以成功,索性F12查看到底执行的请求是啥。然后整理成了Spring后台请求代码。经过验证成功。

java代码:


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import java.io.IOException;
import java.util.*;

import static org.toilelibre.libe.curl.Curl.curl;


/**
 * @author mason
 * @version 1.0
 * @description: TODO
 * @date 2023/6/14 15:49
 */
@Component
public class GeoWebCache {

    private static String url;
    private static String geoUsername;
    private static String geoPassword;
    //工作区间
    public static String workspace;

    @Value("${geoserver.url}")
    public void setUrl(String url) {
        GeoWebCache.url = url;
    }

    @Value("${geoserver.username}")
    public void setGeoUsername(String geoUsername) {
        GeoWebCache.geoUsername = geoUsername;
    }

    @Value("${geoserver.password}")
    public void setGeoPassword(String geoPassword) {
        GeoWebCache.geoPassword = geoPassword;
    }

    @Value("${geoserver.workspace}")
    public void setWorkspace(String workspace) {
        GeoWebCache.workspace = workspace;
    }


    /**
     * 获取geoWebCache中的图层
     *
     * @return List
     */
    public static List<String> getLayers() {
        Map<String, List<String>> map = new HashMap();
        String cmd = "curl -u " + geoUsername + ":" + geoPassword + " \"" + url + "/gwc/rest/layers\"";
        HttpResponse curl = curl(cmd);
        HttpEntity entity = curl.getEntity();
        List<String> list = new ArrayList<>();
        if (entity != null) {
            try {
                String result = EntityUtils.toString(entity, "UTF-8");
                JSONArray jsonArray = JSONArray.parseArray(result);
                String jsonString = JSONArray.toJSONString(jsonArray);
                list = JSONArray.parseArray(jsonString, String.class);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return list;
    }


    /**
     * 指定图层进行切片操作
     * @param param
     * @return
     */
    public static R<Void> slice(String layer, @Validated GeoWebCacheParam param) {
        Map<String, String> requestMap = JSON.parseObject(JSON.toJSONString(param), HashMap.class);
        HashMap<String, String> headerMap = new HashMap<>();
        headerMap.put("Authorization", "Basic " + Base64.getUrlEncoder().encodeToString((geoUsername + ":" + geoPassword).getBytes()));
        String result = null;
        try {
            result = HttpRequestUtil.post(requestMap, url + "/gwc/rest/seed/" + layer , headerMap, "application/x-www-form-urlencoded");
        } catch (Exception e) {
            return R.fail("请求切片失败:" + e);
        }
        return R.ok(result);
    }

    /**
     * 获取切片的情况
     *
     * @param layer 指定图层
     * @return Map
     */
    public static R<Object> getSliceStatus(String layer) {
        //返回所有图层切片情况   curl -u <user>:<password> -XGET http://localhost:8080/geoserver/gwc/rest/seed.json
        //返回指定图层的切片情况
        String cmd = "curl  -u " + geoUsername + ":" + geoPassword + " -XGET " + url + "/gwc/rest/seed/" + layer + ".json";
        HttpResponse curl = curl(cmd);
        StatusLine statusLine = curl.getStatusLine();
        if ("HTTP/1.1 200 OK".equals(statusLine.toString())) {
            HttpEntity entity = curl.getEntity();
            try {
                String result = EntityUtils.toString(entity, "UTF-8");
                return R.ok(result);
            } catch (IOException e) {
                return R.fail("解析返回结果失败:" + e);
            }
        }
        return R.fail("查询失败");
    }





}

 yml中的geoserver配置:

 请求工具类:

/**
 * @author mason
 * @version 1.0
 * @description: TODO
 * @date 2023/6/15 11:11
 */

import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;




/**
 * http请求工具类
 * @ClassName:HttpRequestUtil
 * @Description: Http请求
 */
public class HttpRequestUtil {
    private String defaultContentEncoding;

    public HttpRequestUtil() {
        this.defaultContentEncoding = Charset.defaultCharset().name();
    }

    /**
     * 默认的响应字符集
     */
    public String getDefaultContentEncoding() {
        return this.defaultContentEncoding;
    }

    /**
     * 设置默认的响应字符集
     */
    public void setDefaultContentEncoding(String defaultContentEncoding) {
        this.defaultContentEncoding = defaultContentEncoding;
    }

    public static String post(JSONObject json, String url) throws Exception{
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost post = new HttpPost(url);
        CloseableHttpResponse  response = null;
        InputStream in = null;
        BufferedReader br = null;
        String result = "";
        try {
            StringEntity s = new StringEntity(json.toString(),"utf-8");
            s.setContentEncoding("UTF-8");
            /*发送json数据需要设置contentType*/
            s.setContentType("application/json");
            post.setEntity(s);
            post.setHeader("Content-Type","application/json;charset=utf-8");
            response = httpclient.execute(post);
            in = response.getEntity().getContent();
            br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                if(StringUtils.isBlank(result)) result = "服务器异常";
                throw new Exception(result);
            }
            // System.out.println("返回数据="+result);
        } catch (Exception e) {
            //System.err.println("调用接口出错::::::::::::"+e.getMessage());
            throw new Exception(e.getMessage());
        } finally {
            if(null != br) br.close();
            if(null != br) in.close();
            if(null != response) response.close();
            if(null != httpclient) httpclient.close();
        }
        return result;
    }

    public static String post(JSONObject json, String url, Map<String, String> headerMap) throws Exception{
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost post = new HttpPost(url);
        CloseableHttpResponse  response = null;
        InputStream in = null;
        BufferedReader br = null;
        String result = "";
        try {
            StringEntity s = new StringEntity(json.toString(),"utf-8");
            s.setContentEncoding("UTF-8");
            /*发送json数据需要设置contentType*/
            s.setContentType("application/json");
            post.setEntity(s);
            post.setHeader("Content-Type","application/json;charset=utf-8");
            Set<Entry<String, String>> headerEntries = headerMap.entrySet();
            for (Entry<String, String> headerEntry:headerEntries){
                post.setHeader(headerEntry.getKey(), headerEntry.getValue());
            }
            response = httpclient.execute(post);
            in = response.getEntity().getContent();
            br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                if(StringUtils.isBlank(result)) result = "服务器异常";
                throw new Exception(result);
            }
            //System.out.println("返回数据="+result);
        } catch (Exception e) {
            //System.err.println("调用接口出错::::::::::::"+e.getMessage());
            throw new Exception(e.getMessage());
        } finally {
            br.close();
            in.close();
            response.close();
            httpclient.close();
        }
        return result;
    }

    /**
     * ContentType.URLENCODED.getHeader()
     * @param map
     * @param url
     * @param headerMap
     * @param contentType
     * @return
     * @throws Exception
     */
    public static String post(Map<String, String> map, String url, Map<String, String> headerMap, String contentType) throws Exception{
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost post = new HttpPost(url);
        CloseableHttpResponse  response = null;
        InputStream in = null;
        BufferedReader br = null;
        String result = "";
        try {
            List<NameValuePair> nameValuePairs = getNameValuePairList(map);
            UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nameValuePairs, "UTF-8");
            /*发送json数据需要设置contentType*/
            urlEncodedFormEntity.setContentType(contentType);
            post.setEntity(urlEncodedFormEntity);
            post.setHeader("Content-Type", contentType);
            Set<Entry<String, String>> headerEntries = headerMap.entrySet();
            for (Entry<String, String> headerEntry:headerEntries){
                post.setHeader(headerEntry.getKey(), headerEntry.getValue());
            }
            response = httpclient.execute(post);
            in = response.getEntity().getContent();
            br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                if(StringUtils.isBlank(result)) result = "服务器异常";
                throw new Exception(result);
            }
            //System.out.println("返回数据="+result);
        } catch (Exception e) {
            //System.err.println("调用接口出错::::::::::::"+e.getMessage());
            throw new Exception(e.getMessage());
        } finally {
            br.close();
            in.close();
            response.close();
            httpclient.close();
        }
        return result;
    }

    private static List<NameValuePair> getNameValuePairList(Map<String, String> map) {
        List<NameValuePair> list = new ArrayList<>();
        for(String key : map.keySet()) {
            list.add(new BasicNameValuePair(key,map.get(key)));
        }

        return list;
    }

    public static String post(String params, String url, Map<String, String> headerMap) throws Exception{
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost post = new HttpPost(url);
        CloseableHttpResponse  response = null;
        InputStream in = null;
        BufferedReader br = null;
        String result = "";
        try {
            StringEntity s = new StringEntity(params.toString(),"utf-8");
            s.setContentEncoding("UTF-8");
            /*发送json数据需要设置contentType*/
            s.setContentType("application/json");
            post.setEntity(s);
            post.setHeader("Content-Type","application/json;charset=utf-8");
            Set<Entry<String, String>> headerEntries = headerMap.entrySet();
            for (Entry<String, String> headerEntry:headerEntries){
                post.setHeader(headerEntry.getKey(), headerEntry.getValue());
            }
            response = httpclient.execute(post);
            in = response.getEntity().getContent();
            br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                if(StringUtils.isBlank(result)) result = "服务器异常";
                throw new Exception(result);
            }
            //System.out.println("返回数据="+result);
        } catch (Exception e) {
            //System.err.println("调用接口出错::::::::::::"+e.getMessage());
            throw new Exception(e.getMessage());
        } finally {
            br.close();
            in.close();
            response.close();
            httpclient.close();
        }
        return result;
    }

    public static String put(JSONObject json, String url, Map<String, String> headerMap) throws Exception {
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPut post = new HttpPut(url);
        CloseableHttpResponse  response = null;
        InputStream in = null;
        BufferedReader br = null;
        String result = "";
        try {
            StringEntity s = new StringEntity(json.toString(),"utf-8");
            s.setContentEncoding("UTF-8");
            /*发送json数据需要设置contentType*/
            s.setContentType("application/json");
            post.setEntity(s);
            post.setHeader("Content-Type","application/json;charset=utf-8");
            Set<Entry<String, String>> headerEntries = headerMap.entrySet();
            for (Entry<String, String> headerEntry:headerEntries){
                post.setHeader(headerEntry.getKey(), headerEntry.getValue());
            }
            response = httpclient.execute(post);
            in = response.getEntity().getContent();
            br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                if(StringUtils.isBlank(result)) result = "服务器异常";
                throw new Exception(result);
            }
            //System.out.println("返回数据="+result);
        } catch (Exception e) {
            //System.err.println("调用接口出错::::::::::::"+e.getMessage());
            throw new Exception(e.getMessage());
        } finally {
            br.close();
            in.close();
            response.close();
            httpclient.close();
        }
        return result;
    }

    public static String delete(String url, Map<String, String> headerMap) throws Exception {
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpDelete post = new HttpDelete(url);
        CloseableHttpResponse  response = null;
        InputStream in = null;
        BufferedReader br = null;
        String result = "";
        try {
            post.setHeader("Content-Type","application/json;charset=utf-8");
            Set<Entry<String, String>> headerEntries = headerMap.entrySet();
            for (Entry<String, String> headerEntry:headerEntries){
                post.setHeader(headerEntry.getKey(), headerEntry.getValue());
            }
            response = httpclient.execute(post);
            in = response.getEntity().getContent();
            br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                if(StringUtils.isBlank(result)) result = "服务器异常";
                throw new Exception(result);
            }
            //System.out.println("返回数据="+result);
        } catch (Exception e) {
            //System.err.println("调用接口出错::::::::::::"+e.getMessage());
            throw new Exception(e.getMessage());
        } finally {
            br.close();
            in.close();
            response.close();
            httpclient.close();
        }
        return result;
    }

    public static String get(JSONObject paramsObj, String url, Map<String, String> headerMap) throws Exception {
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        CloseableHttpResponse  response = null;
        InputStream in = null;
        BufferedReader br = null;
        String result = "";
        try {
            StringBuffer param = new StringBuffer();
            int i = 0;

            Set<Entry<String, Object>> entries = paramsObj.entrySet();
            for (Entry<String, Object> entry:entries){
                if (i == 0)
                    param.append("?");
                else
                    param.append("&");
                param.append(entry.getKey()).append("=").append(entry.getValue());
                i++;
            }

            url += param;
            HttpGet post = new HttpGet(url);
//            post.setHeader("Content-Type","application/json;charset=utf-8");

            Set<Entry<String, String>> headerEntries = headerMap.entrySet();
            for (Entry<String, String> headerEntry:headerEntries){
                post.setHeader(headerEntry.getKey(), headerEntry.getValue());
            }

            response = httpclient.execute(post);
            in = response.getEntity().getContent();
            br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                if(StringUtils.isBlank(result)) result = "服务器异常";
                throw new Exception(result);
            }
            //System.out.println("返回数据="+result);
        } catch (Exception e) {
            // System.err.println("调用接口出错::::::::::::"+e.getMessage());
            throw new Exception(e.getMessage());
        } finally {
            br.close();
            in.close();
            response.close();
            httpclient.close();
        }
        return result;
    }
}

控制层代码:

import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * @author mason
 * @version 1.0
 * @description: TODO
 * @date 2023/6/14 16:11
 */
@AllArgsConstructor
@RequestMapping("/gwc")
@RestController
public class GeoWebCacheController {

    /**
     * 获取geoWebCache中的图层
     * @return
     */
    @GetMapping("/getLayers")
    public List<String> getLayers() {
        return GeoWebCache.getLayers();
    }

    /**
     * 指定图层进行切片操作
     * @param layer 图层名称
     * @param param 请求参数
     * @return
     */
    @PostMapping("/slice")
    public R<Void> slice(String layer, GeoWebCacheParam param) {
        return GeoWebCache.slice(layer, param);
    }

    /**
     * 获取切片的情况
     *
     * @param layer 指定图层
     * @return Map
     */
    @GetMapping("/getSliceType")
    public R<Object> getSliceType(@RequestParam String layer) {
        return GeoWebCache.getSliceStatus(layer);
    }

}

请求参数:

import com.sw.survey.common.validate.AddGroup;
import com.sw.survey.common.validate.EditGroup;
import lombok.Data;

import javax.validation.constraints.NotBlank;

/**
 * @author mason
 * @version 1.0
 * @description: GeoWebCache切片请求参数
 * @date 2023/6/15 11:14
 */
@Data
public class GeoWebCacheParam {

    /**
     * 执行切片的线程数
     */
    @NotBlank(message = "执行切片的线程数不能为空", groups = { AddGroup.class, EditGroup.class })
    private String threadCount;

    /**
     * 执行任务的类型:seed:切片,reseed:重新执行切片,truncate删除切片文件
     */
    private String type;

    /**
     * 坐标系,例如:EPSG:4491
     */
    private String gridSetId;

    /**
     *切片的结果类型,例如:image/png
     */
    private String tileFormat;

    /**
     * 切片文件的最小缩放层级
     */
    private String zoomStart;

    /**
     * 切片文件的最大缩放层级
     */
    private String zoomStop;

    /**
     * 图层样式
     */
    private String parameter_STYLES;

    /**
     * 切片请求失败重试次数
     */
    private String tileFailureRetryCount;

    /**
     * 切片请求失败后过多少毫秒再尝试
     */
    private String tileFailureRetryWaitTime;

    /**
     * 中止前的总失败次数
     */
    private String totalFailuresBeforeAborting;

    /**
     * 边界:最小X坐标
     */
    private String minX;

    /**
     * 边界:最小Y坐标
     */
    private String minY;

    /**
     * 边界:最大X坐标
     */
    private String maxX;

    /**
     * 边界:最大Y坐标
     */
    private String maxY;

}

模拟请求(apifox):

至此,大功告成!

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值