伟的小站 http://www.itfsw.com/blog 因为简单所以纯粹 Wed, 21 Oct 2020 05:48:09 +0000 zh-CN hourly 1 https://wordpress.org/?v=5.5.1 cnpm导致postcss-pxtorem不生效问题 http://www.itfsw.com/blog/post/2020/10/21/cnpm-daozhi-postcss-pxtorem-bushengxiao/ http://www.itfsw.com/blog/post/2020/10/21/cnpm-daozhi-postcss-pxtorem-bushengxiao/#respond Wed, 21 Oct 2020 05:47:11 +0000 http://www.itfsw.com/blog/?p=801 最近一个手机web端使用postcss-pxtorem解决像素坐标转rem的问题,由于使用了vant,而UI出 […]

The post cnpm导致postcss-pxtorem不生效问题 appeared first on 伟的小站.

]]>
最近一个手机web端使用postcss-pxtorem解决像素坐标转rem的问题,由于使用了vant,而UI出图按标准750px像素宽出图。所以为了兼容vant 375px像素宽,所以增加了postcss.config.js 分别对vant模块和项目本身设置不同的基准值,本地开发都没有问题,但是正式环境通过Jenkins打包时发现vant组件整体都缩小了一倍,明显是postcss.config.js 没有生效。

const { sep } = require('path')

module.exports = ({ file }) => {
  // 解决vant设计稿宽度为350px而项目设计稿宽度为750px的问题
  const rootValue = file.dirname.indexOf('node_modules' + sep + 'vant') !== -1 ? 37.5 : 75

  return {
    plugins: {
      autoprefixer: {
        overrideBrowserslist: [
          'Android 4.1',
          'iOS 7.1',
          'Chrome > 31',
          'ff > 31',
          'ie >= 8'
        ]
      },
      'postcss-pxtorem': {
        rootValue: rootValue,
        propList: ['*']
      }
    }
  }
}

修改配置打印编译时具体文件路径信息,看对应rootValue是否计算错误:

// 解决vant设计稿宽度为350px而项目设计稿宽度为750px的问题
  const rootValue = () => {
    console.log(file.dirname, 'node_modules' + sep + 'vant')
    return file.dirname.indexOf('node_modules' + sep + 'vant') !== -1 ? 37.5 : 75
  }

正式环境上发现编译时使用的vant js却是输出如下日志:

/root/svn/workspace/qa_xxxxx/node_modules/_vant@2.10.10@vant/lib node_modules/vant

目录变成奇怪的node_modules/_vant@2.10.10@vant/lib目录,而不是正常node_modules/vant 重新检查了Jenkins配置,发现其打包时使用了cnpm打包,我们知道国内npm比较慢所以很多运维这块使用了cnpm进行了项目打包。但是对于研发来说其实比较拒绝使用该镜像库,因为其对依赖管理做了自己的定制老是爱出一些奇葩问题,问题找到改为npm打包解决!!!!

The post cnpm导致postcss-pxtorem不生效问题 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/10/21/cnpm-daozhi-postcss-pxtorem-bushengxiao/feed/ 0
基于GDAL的Java遥感影像瓦片切片生成方式 http://www.itfsw.com/blog/post/2020/09/08/jiyu-gdal-java-yaogan-yingxiang-wapian-qiepian-shengcheng/ http://www.itfsw.com/blog/post/2020/09/08/jiyu-gdal-java-yaogan-yingxiang-wapian-qiepian-shengcheng/#respond Tue, 08 Sep 2020 07:45:29 +0000 http://www.itfsw.com/blog/?p=765 感谢https://www.jianshu.com/p/98389e3168e8 提供了基础代码实现方式,但由 […]

The post 基于GDAL的Java遥感影像瓦片切片生成方式 appeared first on 伟的小站.

]]>
感谢https://www.jianshu.com/p/98389e3168e8 提供了基础代码实现方式,但由于作者只提供了代码片段,导致某些实现不完整,这里对其补充了下。同时我们常用web地图引擎都是基于Web墨卡托切片,所以在对方实现上增加了投影转换。

package com.itfsw.example;

import org.gdal.gdal.Band;
import org.gdal.gdal.Dataset;
import org.gdal.gdal.Driver;
import org.gdal.gdal.gdal;
import org.gdal.gdalconst.gdalconst;
import org.gdal.gdalconst.gdalconstConstants;
import org.gdal.osr.SpatialReference;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.Coordinate;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;

import java.io.File;
import java.util.UUID;

public class Tif2Tiles {
    /**
     * 瓦片大小
     */
    public final static int TILE_SIZE = 256;
    /**
     * web 墨卡托 EPSG 编码
     */
    public final static int WEB_MERCATOR_EPSG_CODE = 3857;
    /**
     * Wgs84 EPSG 编码
     */
    public final static int WGS_84_EGPS_CODE = 4326;

    /**
     * Tif 切片
     *
     * @param tifFile
     * @param outputDir
     * @param minZoom
     * @param maxZoom
     */
    public static void tif2Tiles(String tifFile, String outputDir, int minZoom, int maxZoom) throws FactoryException, TransformException {
        Dataset srcDs = gdal.Open(tifFile, gdalconstConstants.GA_ReadOnly);
        if (srcDs == null) {
            System.err.println("GDALOpen failed - " + gdal.GetLastErrorNo());
            System.err.println(gdal.GetLastErrorMsg());
            return;
        }
        // 1. 影像重投影到web墨卡托
        String tmpFile = System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID().toString();
        SpatialReference spatialReference = new SpatialReference();
        spatialReference.ImportFromEPSG(WEB_MERCATOR_EPSG_CODE);
        Dataset webMercatorDs = gdal.AutoCreateWarpedVRT(srcDs, null, spatialReference.ExportToWkt(), gdalconst.GRA_Bilinear);
        Dataset dataset = gdal.GetDriverByName("GTiff").CreateCopy(tmpFile, webMercatorDs);
        srcDs.delete();
        webMercatorDs.delete();

        try {
            // 2. 获取遥感影像经纬度范围,计算遥感影像像素分辨率
            // 获取原始影像的地理坐标范围
            double[] geoTransform = dataset.GetGeoTransform();
            // 获取原始影像的像素分辨率
            int xSize = dataset.getRasterXSize();
            int ySize = dataset.getRasterYSize();
            // 计算经纬度范围
            double lngMin = geoTransform[0];
            double latMax = geoTransform[3];
            double lngMax = lngMin + (xSize * geoTransform[1]) + (ySize * geoTransform[2]);
            double latMin = latMax + (xSize * geoTransform[4]) + (ySize * geoTransform[5]);

            // EPSG:3857 坐标转Wgs84经纬度
            CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:" + WEB_MERCATOR_EPSG_CODE);
            CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:" + WGS_84_EGPS_CODE);
            MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
            Coordinate lngLatMax = JTS.transform(new Coordinate(lngMax, latMax), null, transform);
            Coordinate lngLatMin = JTS.transform(new Coordinate(lngMin, latMin), null, transform);
            lngMax = lngLatMax.getY();
            latMax = lngLatMax.getX();
            lngMin = lngLatMin.getY();
            latMin = lngLatMin.getX();

            // 原始图像东西方向像素分辨率
            double srcWePixelResolution = (lngMax - lngMin) / xSize;
            // 原始图像南北方向像素分辨率
            double srcNsPixelResolution = (latMax - latMin) / ySize;

            for (int zoom = minZoom; zoom <= maxZoom; zoom++) {
                // 3. 根据原始影像地理范围求解切片行列号
                int[] tilePointMax = coordinates2tile(lngMax, latMax, zoom);
                int[] tilePointMin = coordinates2tile(lngMin, latMin, zoom);
                int tileRowMax = tilePointMin[1];
                int tileColMax = tilePointMax[0];
                int tileRowMin = tilePointMax[1];
                int tileColMin = tilePointMin[0];
                for (int row = tileRowMin; row <= tileRowMax; row++) {
                    for (int col = tileColMin; col <= tileColMax; col++) {
                        // 4. 求原始影像地理范围与指定缩放级别指定行列号的切片交集
                        double tempLatMin = tile2lat(row + 1, zoom);
                        double tempLatMax = tile2lat(row, zoom);
                        double tempLonMin = tile2lng(col, zoom);
                        double tempLonMax = tile2lng(col + 1, zoom);


                        ReferencedEnvelope tileBound = new ReferencedEnvelope(tempLonMin, tempLonMax, tempLatMin, tempLatMax, DefaultGeographicCRS.WGS84);
                        ReferencedEnvelope imageBound = new ReferencedEnvelope(lngMin, lngMax, latMin, latMax, DefaultGeographicCRS.WGS84);
                        ReferencedEnvelope intersect = tileBound.intersection(imageBound);

                        // 5. 求解当前切片的像素分辨率(默认切片大小为256*256)
                        // 切片东西方向像素分辨率
                        double dstWePixelResolution = (tempLonMax - tempLonMin) / 256;
                        // 切片南北方向像素分辨率
                        double dstNsPixelResolution = (tempLatMax - tempLatMin) / 256;
                        // 6. 计算交集的像素信息
                        // 求切图范围和原始图像交集的起始点像素坐标
                        int offsetX = (int) ((intersect.getMinX() - lngMin) / srcWePixelResolution);
                        int offsetY = (int) Math.abs((intersect.getMaxY() - latMax) / srcNsPixelResolution);

                        // 求在切图地理范围内的原始图像的像素大小
                        int blockXSize = (int) ((intersect.getMaxX() - intersect.getMinX()) / srcWePixelResolution);
                        int blockYSize = (int) ((intersect.getMaxY() - intersect.getMinY()) / srcNsPixelResolution);

                        // 求原始图像在切片内的像素大小
                        int imageXBuf = (int) Math.ceil((intersect.getMaxX() - intersect.getMinX()) / dstWePixelResolution);
                        int imageYBuf = (int) Math.ceil(Math.abs((intersect.getMaxY() - intersect.getMinY()) / dstNsPixelResolution));

                        // 求原始图像在切片中的偏移坐标
                        int imageOffsetX = (int) ((intersect.getMinX() - tempLonMin) / dstWePixelResolution);
                        int imageOffsetY = (int) Math.abs((intersect.getMaxY() - tempLatMax) / dstNsPixelResolution);
                        imageOffsetX = imageOffsetX > 0 ? imageOffsetX : 0;
                        imageOffsetY = imageOffsetY > 0 ? imageOffsetY : 0;

                        // 7. 使用GDAL的ReadRaster方法对影像指定范围进行读取与压缩。
                        // 推荐在切片前建立原始影像的金字塔文件,ReadRaster在内部实现中可直接读取相应级别的金字塔文件,提高效率。
                        Band inBand1 = dataset.GetRasterBand(1);
                        Band inBand2 = dataset.GetRasterBand(2);
                        Band inBand3 = dataset.GetRasterBand(3);

                        int[] band1BuffData = new int[TILE_SIZE * TILE_SIZE * gdalconst.GDT_Int32];
                        int[] band2BuffData = new int[TILE_SIZE * TILE_SIZE * gdalconst.GDT_Int32];
                        int[] band3BuffData = new int[TILE_SIZE * TILE_SIZE * gdalconst.GDT_Int32];

                        inBand1.ReadRaster(offsetX, offsetY, blockXSize, blockYSize, imageXBuf, imageYBuf, gdalconst.GDT_Int32, band1BuffData, 0, 0);
                        inBand2.ReadRaster(offsetX, offsetY, blockXSize, blockYSize, imageXBuf, imageYBuf, gdalconst.GDT_Int32, band2BuffData, 0, 0);
                        inBand3.ReadRaster(offsetX, offsetY, blockXSize, blockYSize, imageXBuf, imageYBuf, gdalconst.GDT_Int32, band3BuffData, 0, 0);

                        //  8. 将切片数据写入文件
                        // 使用gdal的MEM驱动在内存中创建一块区域存储图像数组
                        Driver memDriver = gdal.GetDriverByName("MEM");
                        Dataset msmDS = memDriver.Create("msmDS", 256, 256, 4);
                        Band dstBand1 = msmDS.GetRasterBand(1);
                        Band dstBand2 = msmDS.GetRasterBand(2);
                        Band dstBand3 = msmDS.GetRasterBand(3);

                        // 设置alpha波段数据,实现背景透明
                        Band alphaBand = msmDS.GetRasterBand(4);
                        int[] alphaData = new int[256 * 256 * gdalconst.GDT_Int32];
                        for (int index = 0; index < alphaData.length; index++) {
                            if (band1BuffData[index] > 0) {
                                alphaData[index] = 255;
                            }
                        }
                        // 写各个波段数据
                        dstBand1.WriteRaster(imageOffsetX, imageOffsetY, imageXBuf, imageYBuf, band1BuffData);
                        dstBand2.WriteRaster(imageOffsetX, imageOffsetY, imageXBuf, imageYBuf, band2BuffData);
                        dstBand3.WriteRaster(imageOffsetX, imageOffsetY, imageXBuf, imageYBuf, band3BuffData);
                        alphaBand.WriteRaster(imageOffsetX, imageOffsetY, imageXBuf, imageYBuf, alphaData);

                        String pngPath = outputDir + File.separator + zoom + "-" + col + "-" + row + ".png";
                        // 使用PNG驱动将内存中的图像数组写入文件
                        Driver pngDriver = gdal.GetDriverByName("PNG");
                        Dataset pngDs = pngDriver.CreateCopy(pngPath, msmDS);

                        // 释放内存
                        msmDS.FlushCache();
                        msmDS.delete();
                        pngDs.delete();
                    }
                }
            }
        } finally {
            // 释放并删除临时文件
            dataset.delete();
            new File(tmpFile).delete();
        }
    }

    /**
     * 经纬度坐标转瓦片坐标
     *
     * @param lng
     * @param lat
     * @param zoom
     * @return
     */
    public static int[] coordinates2tile(double lng, double lat, int zoom) {
        int x = (int) Math.floor((lng + 180) / 360 * (1 << zoom));
        int y = (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * (1 << zoom));
        if (x < 0) {
            x = 0;
        }
        if (x >= (1 << zoom)) {
            x = ((1 << zoom) - 1);
        }
        if (y < 0) {
            y = 0;
        }
        if (y >= (1 << zoom)) {
            y = ((1 << zoom) - 1);
        }
        return new int[]{x, y};
    }

    /**
     * 瓦片坐标转经度
     *
     * @param x
     * @param z
     * @return
     */
    public static double tile2lng(double x, int z) {
        return x / Math.pow(2.0, z) * 360.0 - 180;
    }

    /**
     * 瓦片坐标转纬度
     *
     * @param y
     * @param z
     * @return
     */
    public static double tile2lat(double y, int z) {
        double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
        return Math.toDegrees(Math.atan(Math.sinh(n)));
    }

    /**
     * @param args
     * @throws FactoryException
     * @throws TransformException
     */
    public static void main(String[] args) throws FactoryException, TransformException {
        gdal.AllRegister();
        tif2Tiles("D:/test.tif", "D:/tiles", 3, 15);
    }
}

The post 基于GDAL的Java遥感影像瓦片切片生成方式 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/09/08/jiyu-gdal-java-yaogan-yingxiang-wapian-qiepian-shengcheng/feed/ 0
Error in “PROJCS”: Parameter “EXTENSION” was not expected. http://www.itfsw.com/blog/post/2020/09/08/error-in-projcs-parameter-extension-was-not-expected/ http://www.itfsw.com/blog/post/2020/09/08/error-in-projcs-parameter-extension-was-not-expected/#respond Tue, 08 Sep 2020 06:36:55 +0000 http://www.itfsw.com/blog/?p=761 在使用gt-epsg-hsql进行坐标转换时,某些遥感数据会出现Error in “PROJCS& […]

The post Error in “PROJCS”: Parameter “EXTENSION” was not expected. appeared first on 伟的小站.

]]>
在使用gt-epsg-hsql进行坐标转换时,某些遥感数据会出现Error in “PROJCS”: Parameter “EXTENSION” was not expected.报错的原因是我们需要获取原始遥感影像的坐标系信息,所以使用了CRS.parseWKT(wkt),而我们wkt通过osr库处理过都带上了EXTENSION信息,导致报错。

PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]

目前官方已经记录了这个问题,并且会在将来24-RC版本中进行修复。目前版本只能通过正则替换掉该附加信息。

wkt = wkt.replaceFirst(",EXTENSION\\[.*?\\]", "");

The post Error in “PROJCS”: Parameter “EXTENSION” was not expected. appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/09/08/error-in-projcs-parameter-extension-was-not-expected/feed/ 0
Windows下搭建GDAL 3.x版本java开发环境 http://www.itfsw.com/blog/post/2020/09/07/windows-xia-dajian-gdal-3-x-banben-java-kaifa-huanjing/ http://www.itfsw.com/blog/post/2020/09/07/windows-xia-dajian-gdal-3-x-banben-java-kaifa-huanjing/#respond Mon, 07 Sep 2020 05:48:26 +0000 http://www.itfsw.com/blog/?p=752 GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下 […]

The post Windows下搭建GDAL 3.x版本java开发环境 appeared first on 伟的小站.

]]>
GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下的开源栅格空间数据转换库。它利用抽象数据模型来表达所支持的各种文件格式,目前很多遥感处理工具底层其实都是利用该库进行的封装。

GDAL本身是基于C\C++编写的,而遥感处理领域大家都习惯使用Python进行编码,所以网上教程都以这两种语言为主,但是项目团队成员都是Java程序员所以在配置开发环境时遇到很多问题,特记录下我们在开发中环境配置过程,方便后续参考。

现在网上大多数教程都是以2.x版本为主,或者模糊版本界限,导致很多人安装时都不对,而且很多教程中都涉及到拷贝动态库文件.dll或者需要把相应jar包导入项目的问题,但我们实际使用中应该避免这种方式(和运行环境强绑定)。

下面教程是已引入官方maven依赖,同时相关动态库以环境变量方式引入项目:

一、去gisinternals下载已经编译好对应c++版本的安装包。

下载地址:http://download.gisinternals.com/release.php ,需要注意下载的版本所对应的c++版本,如MSVC 2017则本机应该安装对应c++库。进入下载页面后下载core.msi安装包。

二、配置GDAL环境变量。

安装好GDAL安装包后(64位版本默认安装在C:\Program Files\GDAL),到Windows环境变量配置相应环境变量。

1. GDAL动态库地址加入path变量。

2. GDAL_DATAGDAL_DRIVER_PATH环境变量。

3. 如果你需要使用osr相关功能,一定记住增加PROJ_LIB环境变量。

三、maven项目引入GDAL依赖。

maven项目中引入对应大版本依赖,GDAL是通过swig对其他语言提供的支持,官方依赖中只上传了大版本变化的依赖。比如我们安装的是3-1-2版本,依赖我们引入3.1.0是没有问题的。

<dependency>
    <groupId>org.gdal</groupId>
    <artifactId>gdal</artifactId>
    <version>3.1.0</version>
</dependency>
gdal.AllRegister();
System.out.println(gdal.VersionInfo());

The post Windows下搭建GDAL 3.x版本java开发环境 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/09/07/windows-xia-dajian-gdal-3-x-banben-java-kaifa-huanjing/feed/ 0
Chrome浏览器对backgroud-size计算bug导致背景图片拼接留缝问题 http://www.itfsw.com/blog/post/2020/08/18/chrome-liulanqi-dui-backgroud-size-jisuan-bug-dao-zhi-beijing-tupian-pinjie-liufeng-wenti/ http://www.itfsw.com/blog/post/2020/08/18/chrome-liulanqi-dui-backgroud-size-jisuan-bug-dao-zhi-beijing-tupian-pinjie-liufeng-wenti/#respond Tue, 18 Aug 2020 09:33:17 +0000 http://www.itfsw.com/blog/?p=682 最近做了一个vue组件,UI出图做了一个异形直角面板,对于直角面板可以使用css3的linear-gradie […]

The post Chrome浏览器对backgroud-size计算bug导致背景图片拼接留缝问题 appeared first on 伟的小站.

]]>
最近做了一个vue组件,UI出图做了一个异形直角面板,对于直角面板可以使用css3的linear-gradient做4幅背景填充实现直角效果,同时要借助backgroud-size对4幅背景做位置和大小调整。具体css样式实现如下:

background: linear-gradient(135deg, transparent 15px, rgba(30, 38, 67, 0.8) 0) top left no-repeat,
    linear-gradient(-135deg, transparent 15px, rgba(30, 38, 67, 0.8) 0) top right no-repeat,
    linear-gradient(-45deg, transparent 15px, rgba(30, 38, 67, 0.8) 0) bottom right no-repeat,
    linear-gradient(45deg, transparent 15px, rgba(30, 38, 67, 0.8) 0) bottom left no-repeat;
background-size: 50% 50%;

本机开发测试都没有问题,但是提供给同事使用时却发现在同事浏览器上背景图片拼接的中间位置多出来一条缝没有填满,而我们对背景大小x轴大小设置的是50%,两幅拼接起来正好填满。在我本机Firefox上填充是正常的,而同事的是Chrome浏览器,难道是浏览器兼容?

再次检查同事使用代码,可疑的地方是他按UI样式对组件宽度设置为291px,难道Chrome计算background-imagebackground-size由于二等分出现小数点对小数点位数处理出现问题?尝试宽度改为290px果然问题解决。

所以推断Chrome对background-size的计算是直接忽略小数点的,这就导致如果无法整除时对背景填充就会出现问题!!!

The post Chrome浏览器对backgroud-size计算bug导致背景图片拼接留缝问题 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/08/18/chrome-liulanqi-dui-backgroud-size-jisuan-bug-dao-zhi-beijing-tupian-pinjie-liufeng-wenti/feed/ 0
logback配置文件读取spring环境变量 http://www.itfsw.com/blog/post/2020/06/18/logback-peizhiwenjian-duqu-spring-huanjingbianliang/ http://www.itfsw.com/blog/post/2020/06/18/logback-peizhiwenjian-duqu-spring-huanjingbianliang/#respond Thu, 18 Jun 2020 07:48:00 +0000 http://www.itfsw.com/blog/?p=669 最近做了一个springboot的本地程序,日志记录使用的是logback。而日志的记录地址是通过applic […]

The post logback配置文件读取spring环境变量 appeared first on 伟的小站.

]]>
最近做了一个springboot的本地程序,日志记录使用的是logback。而日志的记录地址是通过application.yml中配置来确定的,所以我们需要在logback中读取spring的上下文取得配置信息,还好logback已经为我们提供了相关操作springProperty

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="PATTERN" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) |-%-5level [%thread] %c [%L] -| %msg%n"/>
    <!-- 读取spring配置 -->
    <springProperty name="LOG_PATH" source="server.work-space"/>
</configuration>

The post logback配置文件读取spring环境变量 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/06/18/logback-peizhiwenjian-duqu-spring-huanjingbianliang/feed/ 0
CentOS下Docker开启远程TCP:2375访问 http://www.itfsw.com/blog/post/2020/06/12/centos-xia-docker-kaiqi-yuancheng-tcp-2375-fangwen/ http://www.itfsw.com/blog/post/2020/06/12/centos-xia-docker-kaiqi-yuancheng-tcp-2375-fangwen/#respond Fri, 12 Jun 2020 08:51:56 +0000 http://www.itfsw.com/blog/?p=664 目前在CentOS7下开启docker的远程TCP:2375访问,百度了下好多都是在/etc/systemd/ […]

The post CentOS下Docker开启远程TCP:2375访问 appeared first on 伟的小站.

]]>
目前在CentOS7下开启docker的远程TCP:2375访问,百度了下好多都是在/etc/systemd/system/docker.service.d文件夹下创建配置文件,但是不知道是版本的原因还是系统原因都一直没法正常开启。所以只能常规操作修改/lib/systemd/system/docker.service文件。

docker.service文件中找到ExecStart配置,在后面添加-H tcp://0.0.0.0:2375配置后重启docker, lsof -i:2375 发现监听成功!但是目前问题是升级docker版本后该配置就会丢失,需要重新配置。

The post CentOS下Docker开启远程TCP:2375访问 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/06/12/centos-xia-docker-kaiqi-yuancheng-tcp-2375-fangwen/feed/ 0
Jenkins安装Nodejs插件打包Vue项目 http://www.itfsw.com/blog/post/2020/06/12/jenkins-anzhuang-nodejs-chajian-dabao-vue-xiangmu/ http://www.itfsw.com/blog/post/2020/06/12/jenkins-anzhuang-nodejs-chajian-dabao-vue-xiangmu/#respond Fri, 12 Jun 2020 07:33:23 +0000 http://www.itfsw.com/blog/?p=658 一、Jenkins安装配置Nodejs插件 1. 安装Nodejs插件:系统管理->插件管理->可 […]

The post Jenkins安装Nodejs插件打包Vue项目 appeared first on 伟的小站.

]]>
一、Jenkins安装配置Nodejs插件

1. 安装Nodejs插件:系统管理->插件管理->可选插件->搜索nodejs 进行安装

2. 配置Nodejs插件:系统管理->全局工具配置->nodejs 这里你可以安装指定版本nodejs 或者需要进行全局安装的npm包

二、vue项目构建

1. 项目构建环境选中node&npm支持->NodeJS Installation选中刚才配置的Nodejs版本

2. 构建选择执行shell->这里就可以执行我们的vue构建命令了

The post Jenkins安装Nodejs插件打包Vue项目 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/06/12/jenkins-anzhuang-nodejs-chajian-dabao-vue-xiangmu/feed/ 0
通过JTS(Geometry[Polygon]) getArea计算多边形面积、距离、周长等(单位米) http://www.itfsw.com/blog/post/2020/06/11/tongguo-jts-geometry-polygon-getarea-jisuan-duobianxing-mianji-juli-zhouchang-deng-danwei-mi/ http://www.itfsw.com/blog/post/2020/06/11/tongguo-jts-geometry-polygon-getarea-jisuan-duobianxing-mianji-juli-zhouchang-deng-danwei-mi/#respond Thu, 11 Jun 2020 08:55:23 +0000 http://www.itfsw.com/blog/?p=652 JTS包下Geometry子类(Polygon、CircularRing、LineString等)都重写get […]

The post 通过JTS(Geometry[Polygon]) getArea计算多边形面积、距离、周长等(单位米) appeared first on 伟的小站.

]]>
JTS包下Geometry子类(Polygon、CircularRing、LineString等)都重写getArea、distance、getLength等方法实现面积距离周长等计算,但是当我们在使用时往往发现计算出来的值很奇怪,不知道其具体单位是什么。

这里我们先要搞清楚JTS包下的Geometry等类其实是代表的空间几何,它所提供的几何计算只是单纯的值计算,而我们提到的根据GIS经纬度等获取面积周长等其实是需要放到对应坐标系中才有意义。

而我们默认使用经纬度构建的Geometry对象一般都是使用WGS84坐标系初始化的,这时计算出来的值当然很奇怪。所以我们一般在计算面积、距离、周长等值时一般单位都是米,所以我们需要做的第一步是把坐标系转换为墨卡托投影,这时计算出来的单位才是米。

一、引入相应jar包

<!-- geo tools -->
<dependency>
  <groupId>org.locationtech.jts</groupId>
  <artifactId>jts-core</artifactId>
  <version>1.16.1</version>
</dependency>
<dependency>
  <groupId>org.geotools</groupId>
  <artifactId>gt-geojson</artifactId>
  <version>21.2</version>
</dependency>
<!-- epsg 查询 -->
<dependency>
	<groupId>org.geotools</groupId>
	<artifactId>gt-epsg-hsql</artifactId>
	<version>23.0</version>
</dependency>

二、坐标转换和相关计算

public void test(Geometry geometry) throws FactoryException, TransformException {
	// WGS84(一般项目中常用的是CSR:84和EPSG:4326)
	CoordinateReferenceSystem sourceCRS = CRS.decode("CRS:84");
	// Pseudo-Mercator(墨卡托投影)
	CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:3857");
	MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
	Geometry geometryMercator = JTS.transform(geometry, transform);

	// 面积、周长
	System.out.println(geometryMercator.getArea());
	System.out.println(geometryMercator.getLength());
}

The post 通过JTS(Geometry[Polygon]) getArea计算多边形面积、距离、周长等(单位米) appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/06/11/tongguo-jts-geometry-polygon-getarea-jisuan-duobianxing-mianji-juli-zhouchang-deng-danwei-mi/feed/ 0
多边形封闭区域分割方法 http://www.itfsw.com/blog/post/2020/06/09/duobianxing-fengbiquyu-fenge-fangfa/ http://www.itfsw.com/blog/post/2020/06/09/duobianxing-fengbiquyu-fenge-fangfa/#respond Tue, 09 Jun 2020 04:07:24 +0000 http://www.itfsw.com/blog/?p=632 把封闭区域分割成指定的份数,可以使用泰森多边形进行解决。泰森多边形又叫维诺图(Voronoi Diagram) […]

The post 多边形封闭区域分割方法 appeared first on 伟的小站.

]]>
把封闭区域分割成指定的份数,可以使用泰森多边形进行解决。泰森多边形又叫维诺图(Voronoi Diagram),它是对空间平面的一种剖分,其特点是多边形内的任何点到该多边形的控制点的距离最近,而离相邻多边形内控制点的距离较远,且每个多边形内有且仅有一个控制点(具体如下图所示)。

因此,对于前述的建筑物外轮廓,如果建筑物在某层要分成n部分,我们可以随机在其中找n个点做为控制点,然后就可以使用泰森多边形方法将它分成n部分。这就解决我们的封闭区域分割问题。

我们想实现尽可能平均的分割,那么n个控制点就不能随机选取。对于整个封闭区域,可以看成是若干点的集合,我们可以使用k-means算法计算n个聚类中心,将这个点集分成n个子集。这n个聚类中心实际是每个子集的质心,以它们为维诺图中的控制点,分割出的n个部分将更加平均。

上述算法还有一个问题,即如何随机选取若干样点组成点集,来表示封闭区域。我们可以计算封闭区域的最小外包矩形,这个最小外包矩形可以确定选取样点的横坐标和纵坐标的范围。然后在此范围内随机给出样点的横坐标和纵坐标,这样生成的样点一定在最小外包矩形内,但不一定都在封闭区域内。这里我们使用射线法判断生成的点是否在封闭区域内。

在计算机表达中,封闭区域的外轮廓实际是一个多边形。射线法以判断点开始,向右(或向左)的水平方向作一射线,计算该射线与多边形每条边的交点个数,如果交点个数为奇数,则点位于多边形内,偶数则在多边形外。该算法对于复合多边形也能正确判断(如下图所示)。

综上所述,我们的算法如下:

  • 在封闭区域内取m个样点表示整个区域(使用射线法判断随机样点是否在封闭区域内);
  • 使用k-means算法对m个样点进行聚成n类,求出每个子类的聚类中心点;
  • n个聚类中心点做为维诺图的控制点,生成封闭区域的维诺图;
  • 根据生成维诺图分割封闭区域,得到每个分割部分外轮廓的点集合。

具体实现

一、引入相关依赖包

<!-- geo tools -->
<dependency>
	<groupId>org.locationtech.jts</groupId>
	<artifactId>jts-core</artifactId>
	<version>1.16.1</version>
</dependency>
<dependency>
	<groupId>org.geotools</groupId>
	<artifactId>gt-geojson</artifactId>
	<version>21.2</version>
</dependency>
<!-- k-means -->
<dependency>
	<groupId>ca.pjer</groupId>
	<artifactId>ekmeans</artifactId>
	<version>2.0.0</version>
</dependency>

二、实现代码

/**
 * 多边形平分
 *
 * @param polygon 多边形
 * @param count   平分份数
 * @param random  点集数量(kmeans算法用,越多越精确但速度越慢)
 */
public static MultiPolygon splitPolygon(Polygon polygon, int count, int random) {
	GeometryFactory geometryFactory = new GeometryFactory();
	// 1. 构建随机点
	double minY = 90;
	double maxY = -90;
	double minX = 180;
	double maxX = -180;

	for (Coordinate point : polygon.getCoordinates()) {
		// 最小
		if (point.getY() < minY) {
			minY = point.getY();
		}
		if (point.getX() < minX) {
			minX = point.getX();
		}
		// 最大
		if (point.getY() > maxY) {
			maxY = point.getY();
		}
		if (point.getX() > maxX) {
			maxX = point.getX();
		}
	}

	double[][] points = new double[random][2];
	int index = 0;
	do {
		double x = RandomUtils.nextDouble(minX, maxX);
		double y = RandomUtils.nextDouble(minY, maxY);
		if (polygon.contains(geometryFactory.createPoint(new Coordinate(x, y)))) {
			points[index++] = new double[]{x, y};
		}
	} while (index < random);
	// 2. 利用EKmeans 获取分组和簇的质心
	double[][] centroids = new double[count][2];
	EKmeans eKmeans = new EKmeans(centroids, points);
	eKmeans.setEqual(true);
	// 替换距离计算方法,使用基于经纬度的距离计算
	eKmeans.setDistanceFunction((p1, p2) -> new Coordinate(p1[0], p1[1]).distance(new Coordinate(p1[0], p1[1])));
	eKmeans.run();

	// 获取分组id和中心聚合
	centroids = eKmeans.getCentroids();

	// 3. 构建泰森多边形
	List<Coordinate> coords = new ArrayList<>();
	for (double[] p : centroids) {
		coords.add(new Coordinate(p[0], p[1]));
	}

	VoronoiDiagramBuilder voronoiDiagramBuilder = new VoronoiDiagramBuilder();
	Envelope clipEnvelpoe = new Envelope();
	voronoiDiagramBuilder.setSites(coords);
	voronoiDiagramBuilder.setClipEnvelope(clipEnvelpoe);

	Geometry geom = voronoiDiagramBuilder.getDiagram(JTSFactoryFinder.getGeometryFactory());

	// 4. 利用封闭面切割泰森多边形
	Coordinate[] coordinates = polygon.getCoordinates();
	Polygon pyg = geometryFactory.createPolygon(coordinates);

	List<Geometry> geometries = new ArrayList<>();
	for (int i = 0; i < geom.getNumGeometries(); i++) {
		Geometry geometry = geom.getGeometryN(i);
		geometries.add(pyg.intersection(geometry));
	}

	MultiPolygon multiPolygon = geometryFactory.createMultiPolygon(geometries.toArray(new Polygon[0]));

	return multiPolygon;
}
/**
 * Geometry平分
 *
 * @param geometry Geometry
 * @param count    平分份数
 * @param random   点集数量(kmeans算法用,越多越精确但速度越慢)
 * @return
 */
public static List<Geometry> splitGeometry(Geometry geometry, int count, int random) {
	GeometryFactory geometryFactory = new GeometryFactory();
	// 1. 构建随机点
	double minY = 90;
	double maxY = -90;
	double minX = 180;
	double maxX = -180;
	for (Coordinate point : geometry.getCoordinates()) {
		// 最小
		if (point.getY() < minY) {
			minY = point.getY();
		}
		if (point.getX() < minX) {
			minX = point.getX();
		}
		// 最大
		if (point.getY() > maxY) {
			maxY = point.getY();
		}
		if (point.getX() > maxX) {
			maxX = point.getX();
		}
	}
	double[][] points = new double[random][2];
	int index = 0;
	do {
		double x = RandomUtils.nextDouble(minX, maxX);
		double y = RandomUtils.nextDouble(minY, maxY);
		if (geometry.contains(geometryFactory.createPoint(new Coordinate(x, y)))) {
			points[index++] = new double[]{x, y};
		}
	} while (index < random);

	// 2. 利用EKmeans 获取分组和簇的质心
	double[][] centroids = new double[count][2];
	EKmeans eKmeans = new EKmeans(centroids, points);
	eKmeans.setEqual(true);
	// 替换距离计算方法,使用基于经纬度的距离计算
	eKmeans.setDistanceFunction((p1, p2) -> Coordinates.getDistance(new CoordinatesPoint(p1[0], p1[1]), new CoordinatesPoint(p2[0], p2[1])));
	eKmeans.run();

	// 获取分组id和中心聚合
	centroids = eKmeans.getCentroids();

	// 3. 构建泰森多边形
	List<Coordinate> coords = new ArrayList<>();
	for (double[] p : centroids) {
		coords.add(new Coordinate(p[0], p[1]));
	}

	VoronoiDiagramBuilder voronoiDiagramBuilder = new VoronoiDiagramBuilder();
	Envelope clipEnvelpoe = new Envelope();
	voronoiDiagramBuilder.setSites(coords);
	voronoiDiagramBuilder.setClipEnvelope(clipEnvelpoe);

	Geometry geom = voronoiDiagramBuilder.getDiagram(JTSFactoryFinder.getGeometryFactory());

	// 4. 利用封闭面切割泰森多边形
	List<Geometry> geometries = new ArrayList<>();
	for (int i = 0; i < geom.getNumGeometries(); i++) {
		geometries.add(geometry.intersection(geom.getGeometryN(i)));
	}

	return geometries;
}

三、切分结果

The post 多边形封闭区域分割方法 appeared first on 伟的小站.

]]>
http://www.itfsw.com/blog/post/2020/06/09/duobianxing-fengbiquyu-fenge-fangfa/feed/ 0