riku
2021-06-30 5353617c7b2135ab00f98d8e05b2f8dfb2e096ed
1. 新增污染权重分析功能
已修改12个文件
已添加12个文件
1553 ■■■■■ 文件已修改
pom.xml 120 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/common/utils/ExcelUtil.kt 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/bean/CompanySOP.kt 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseDataPrep.kt 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseEffect.kt 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseMData.kt 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseModel.kt 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseSOP.kt 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseSection.kt 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseTag.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/BaseWeight.kt 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/TimeTag.kt 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/EPWDataPrep.kt 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/EPWModel.kt 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/TimeSection.kt 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/WeightType.kt 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/WindDirWeight.kt 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/WindDisWeight.kt 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/log4j2.xml 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/Test.kt 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/model/epw/EPWModelTest.kt 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -17,7 +17,7 @@
    <properties>
        <java.version>1.8</java.version>
        <kotlin.version>1.2.71</kotlin.version>
        <kotlin.version>1.5.0</kotlin.version>
        <!-- tk.mybatis -->
        <mapper.plugin>tk.mybatis.mapper.generator.MapperPlugin</mapper.plugin>
        <mapper.Mapper>tk.mybatis.mapper.common.Mapper</mapper.Mapper>
@@ -42,10 +42,6 @@
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
        </dependency>
        <dependency>
@@ -129,35 +125,61 @@
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-test-junit</artifactId>
            <version>${kotlin.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-maven-plugin -->
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <version>${kotlin.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-maven-allopen -->
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-allopen</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
        <sourceDirectory>src/main/kotlin</sourceDirectory>
        <testSourceDirectory>src/test/kotlin</testSourceDirectory>
        <plugins>
<!--            <plugin>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
<!--            </plugin>-->
<!--            <plugin>-->
<!--                <groupId>org.jetbrains.kotlin</groupId>-->
<!--                <artifactId>kotlin-maven-plugin</artifactId>-->
<!--                <configuration>-->
<!--                    <args>-->
<!--                        <arg>-Xjsr305=strict</arg>-->
<!--                    </args>-->
<!--                    <compilerPlugins>-->
<!--                        <plugin>spring</plugin>-->
<!--                    </compilerPlugins>-->
<!--                </configuration>-->
<!--                <dependencies>-->
<!--                    <dependency>-->
<!--                        <groupId>org.jetbrains.kotlin</groupId>-->
<!--                        <artifactId>kotlin-maven-allopen</artifactId>-->
<!--                        <version>${kotlin.version}</version>-->
<!--                    </dependency>-->
<!--                </dependencies>-->
<!--            </plugin>-->
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <configuration>
                    <args>
                        <arg>-Xjsr305=strict</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <!-- mybatis generator è‡ªåŠ¨ç”Ÿæˆä»£ç æ’ä»¶ -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
@@ -178,6 +200,50 @@
                    </dependency>
                </dependencies>
            </plugin>
<!--            <plugin>-->
<!--                <groupId>org.jetbrains.kotlin</groupId>-->
<!--                <artifactId>kotlin-maven-plugin</artifactId>-->
<!--                <version>${kotlin.version}</version>-->
<!--                <executions>-->
<!--                    <execution>-->
<!--                        <id>compile</id>-->
<!--                        <phase>compile</phase>-->
<!--                        <goals>-->
<!--                            <goal>compile</goal>-->
<!--                        </goals>-->
<!--                    </execution>-->
<!--                    <execution>-->
<!--                        <id>test-compile</id>-->
<!--                        <phase>test-compile</phase>-->
<!--                        <goals>-->
<!--                            <goal>test-compile</goal>-->
<!--                        </goals>-->
<!--                    </execution>-->
<!--                </executions>-->
<!--                <configuration>-->
<!--                    <jvmTarget>1.8</jvmTarget>-->
<!--                </configuration>-->
<!--            </plugin>-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>testCompile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
@@ -186,7 +252,7 @@
        <repository>
            <id>alimaven</id>
            <name>aliyun maven</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
        <repository>
            <id>spring-snapshots</id>
src/main/kotlin/com/flightfeather/uav/common/utils/ExcelUtil.kt
@@ -12,9 +12,13 @@
 */
object ExcelUtil {
    private const val isLog = false
    class MyCell(
            var text: String,
            var rowSpan: Int = 1
            var rowSpan: Int = 1,
            var colSpan: Int = 1,
            var fontColor: Short? = null
    )
    /**
@@ -56,7 +60,7 @@
                                rowspan = c.rowSpan
                            }
                            if (rowspan > 1) {
                                println("合并1-1:$rowIndex;${rowIndex + rowspan - 1};$i")
                                log("合并1-1:$rowIndex;${rowIndex + rowspan - 1};$i")
                                sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, i, i))
                            }
                            if (cell.isEmpty()) {
@@ -67,7 +71,7 @@
                        } else {
                            //当数据不是数组时,需要按最大行数合并单元格
                            if (rowspan > 1) {
                                println("合并1-2:$rowIndex;${rowIndex + rowspan - 1};$i")
                                log("合并1-2:$rowIndex;${rowIndex + rowspan - 1};$i")
                                sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, i, i))
                            }
                            cell
@@ -75,11 +79,11 @@
                when (c) {
                    is MyCell -> {
                        rows.createCell(i).setCellValue(c.text)
                        println("write1-1: ${c.text};($rowIndex, ${i})")
                        log("write1-1: ${c.text};($rowIndex, ${i})")
                    }
                    is String -> {
                        rows.createCell(i).setCellValue(c)
                        println("write1-2: ${c};($rowIndex, ${i})")
                        log("write1-2: ${c};($rowIndex, ${i})")
                    }
                    is Double -> rows.createCell(i).setCellValue(c)
                    is Boolean -> rows.createCell(i).setCellValue(c)
@@ -114,18 +118,170 @@
                            rows = sheet.getRow(_rowIndex) ?: sheet.createRow(_rowIndex)
                        }
                        if (rowspan > 1) {
                            println("合并2:${index};${index + rowspan - 1};$i")
                            log("合并2:${index};${index + rowspan - 1};$i")
                            sheet.addMergedRegion(CellRangeAddress(index, index + rowspan - 1, i, i))
                        }
                        when (c) {
                            is MyCell -> {
                                rows.createCell(map.key).setCellValue(c.text)
                                println("write2-1: ${c.text};($_rowIndex, ${map.key})")
                                log("write2-1: ${c.text};($_rowIndex, ${map.key})")
                            }
                            is String -> {
                                rows.createCell(map.key).setCellValue(c)
                                println("write2-2: ${c};($_rowIndex, ${map.key})")
                                log("write2-2: ${c};($_rowIndex, ${map.key})")
                            }
                            is Double -> rows.createCell(map.key).setCellValue(c)
                            is Boolean -> rows.createCell(map.key).setCellValue(c)
                            is Date -> rows.createCell(map.key).setCellValue(c)
                            is Calendar -> rows.createCell(map.key).setCellValue(c)
                            is LocalDate -> rows.createCell(map.key).setCellValue(c)
                        }
                    }
                }
            }
            rowIndex++
        }
//        workbook.write(out)
//        workbook.close()
//        out.flush()
//        out.close()
    }
    /**
     * è‡ªåŠ¨å¤„ç†è¡Œåˆå¹¶æ•°æ®
     */
    fun write(heads: List<Array<String>>, contents: List<Array<Any>>, workbook: HSSFWorkbook, sheetName: String = "sheet1") {
        val sheet = workbook.createSheet(sheetName)
        println("sheet: $sheetName")
        var rowIndex = 0
        heads.forEach {
            val rows = sheet.createRow(rowIndex)
            for (i in it.indices) {
                rows.createCell(i).setCellValue(it[i])
            }
            rowIndex++
        }
        contents.forEach {
            val maxRow = getMaxRows(it)
            var rows = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex)
            val arrayMap = mutableMapOf<Int, Array<*>>()
            //列序号
            var col = 0
            for (i in it.indices) {
                val cell = it[i]
                var rowspan = maxRow//合并的行的跨度
                var colSpan = 1
                val c =
                    if (cell is Array<*>) {
                        //当数据为数组时,需要根据最大行数重新计算该单元格的行跨度
                        arrayMap[col] = cell
                        rowspan = maxRow / if (cell.size == 0) 1 else cell.size
                        val c = cell[0]
                        if (c is MyCell) {
                            rowspan = c.rowSpan
                            colSpan = c.colSpan
                        }
                        if (rowspan > 1 || colSpan > 1) {
                            log("合并1-1:$rowIndex;${rowIndex + rowspan - 1};$col")
                            sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, col, col + colSpan - 1))
                        }
                        if (cell.isEmpty()) {
                            ""
                        } else {
                            cell[0]
                        }
                    } else {
                        //当数据不是数组时,需要按最大行数合并单元格
                        if (cell is MyCell) {
                            rowspan = cell.rowSpan
                            colSpan = cell.colSpan
                        }
                        if (rowspan > 1 || colSpan > 1) {
                            log("合并1-2:$rowIndex;${rowIndex + rowspan - 1};$col")
                            sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, col, col + colSpan - 1))
                        }
                        cell
                    }
                when (c) {
                    is MyCell -> {
                        rows.createCell(col).apply {
                            c.fontColor?.let {fC ->
                                val font = workbook.createFont()
                                val cellStyle = workbook.createCellStyle()
                                font.color = fC
                                cellStyle.setFont(font)
                                setCellStyle(cellStyle)
                            }
                            setCellValue(c.text)
                        }
                        log("write1-1: ${c.text};($rowIndex, ${col})")
                    }
                    is String -> {
                        rows.createCell(col).setCellValue(c)
                        log("write1-2: ${c};($rowIndex, ${col})")
                    }
                    is Double -> rows.createCell(col).setCellValue(c)
                    is Int -> rows.createCell(col).setCellValue(c.toDouble())
                    is Boolean -> rows.createCell(col).setCellValue(c)
                    is Date -> rows.createCell(col).setCellValue(c)
                    is Calendar -> rows.createCell(col).setCellValue(c)
                    is LocalDate -> rows.createCell(col).setCellValue(c)
                }
                col += colSpan
            }
            for (i in 1 until maxRow) {
                rowIndex++
                arrayMap.forEach { map ->
                    rows = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex)
                    val array = map.value
                    if (i < array.size) {
//                        var rowspan = maxRow / array.size
                        var lastRowSpan = 1
                        var rowspan = 1
                        var colSpan = 1
                        val c = array[i]
                        if (c is MyCell) {
                            rowspan = c.rowSpan
                            colSpan = c.colSpan
                        }
                        val _c = array[i - 1]
                        if (_c is MyCell) {
                            lastRowSpan = _c.rowSpan
                        }
                        var _rowIndex = rowIndex
                        var index = rowIndex
                        if (lastRowSpan > 1) {
                            index = rowIndex + (i * lastRowSpan) - 1
                            _rowIndex = index
                            rows = sheet.getRow(_rowIndex) ?: sheet.createRow(_rowIndex)
                        }
                        if (rowspan > 1 || colSpan > 1) {
                            log("合并2:${index};${index + rowspan - 1};${map.key}")
                            sheet.addMergedRegion(CellRangeAddress(index, index + rowspan - 1, map.key, map.key + colSpan - 1))
                        }
                        when (c) {
                            is MyCell -> {
                                rows.createCell(map.key).setCellValue(c.text)
                                log("write2-1: ${c.text};($_rowIndex, ${map.key})")
                            }
                            is String -> {
                                rows.createCell(map.key).setCellValue(c)
                                log("write2-2: ${c};($_rowIndex, ${map.key})")
                            }
                            is Double -> rows.createCell(map.key).setCellValue(c)
                            is Boolean -> rows.createCell(map.key).setCellValue(c)
@@ -154,4 +310,10 @@
        }
        return maxRows
    }
    private fun log(log: String) {
        if (isLog) {
            println(log)
        }
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/bean/CompanySOP.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,218 @@
package com.flightfeather.uav.lightshare.bean
import com.flightfeather.uav.model.BaseSOP
import java.math.BigDecimal
import java.util.*
/**
 * å·¥ä¸šä¼ä¸šæ±¡æŸ“源信息
 */
class CompanySOP(sourceId: String, sourceName: String, index:String) : BaseSOP(sourceId, sourceName, index) {
    /**
     * ä¸»è¦ä»Žäº‹ä¸šåŠ¡
     */
    var ciMainBusiness: String? = null
    /**
     * æ‰€å±žé›†å›¢
     */
    var ciMemberGroup: String? = null
    /**
     * çœä»½ç¼–码
     */
    var ciProvinceCode: String? = null
    /**
     * çœä»½åç§°
     */
    var ciProvinceName: String? = null
    /**
     * åœ°å¸‚编码
     */
    var ciCityCode: String? = null
    /**
     * åœ°å¸‚名称
     */
    var ciCityName: String? = null
    /**
     * åŒºåŽ¿ç¼–å·
     */
    var ciDistrictCode: String? = null
    /**
     * åŒºåŽ¿åç§°
     */
    var ciDistrictName: String? = null
    /**
     * è¡—镇编码
     */
    var ciTownCode: String? = null
    /**
     * è¡—镇名称
     */
    var ciTownName: String? = null
    /**
     * æ‰€åœ¨å·¥ä¸šåŒº
     */
    var ciIndDistrict: String? = null
    /**
     * å•位地址
     */
    var ciAddress: String? = null
    /**
     * ä¸­å¿ƒç»åº¦
     */
    var ciLongitude: BigDecimal? = null
    /**
     * ä¸­å¿ƒçº¬åº¦
     */
    var ciLatitude: BigDecimal? = null
    /**
     * ç»„织机构代码
     */
    var ciOrgCode: String? = null
    /**
     * æ³•人
     */
    var ciJuridicalPerson: String? = null
    /**
     * è¡Œä¸šç±»åˆ«
     */
    var ciIndClassification: String? = null
    /**
     * è¡Œä¸šä»£ç 
     */
    var ciIndustryCode: String? = null
    /**
     * ç™»è®°æ³¨å†Œç±»åž‹
     */
    var ciRegistrationType: String? = null
    /**
     * æ³¨å†Œèµ„本(万元)
     */
    var ciRegisteredCapital: Int? = null
    /**
     * å»ºåŽ‚å¹´æœˆ
     */
    var ciBuildDate: Date? = null
    /**
     * æœ€æ–°æ”¹æ‰©å»ºå¹´æœˆ
     */
    var ciExpansionDate: Date? = null
    /**
     * èŒå·¥äººæ•°
     */
    var ciWorkersNumber: Int? = null
    /**
     * ä¼ä¸šè§„模
     */
    var ciScale: Byte? = null
    /**
     * åŽ†æ¬¡çŽ¯è¯„å®¡æ‰¹å¹´æœˆ
     */
    var ciEiaApprovarDate: String? = null
    /**
     * æŽ’污许可证编号
     */
    var ciPltPermitCode: String? = null
    /**
     * æŽ’污权交易文件
     */
    var ciTradingFiles: String? = null
    /**
     * é‚®æ”¿ç¼–码
     */
    var ciPostalCode: String? = null
    /**
     * è”系人姓名
     */
    var ciContactName: String? = null
    /**
     * è”系电话
     */
    var ciTelephone: String? = null
    /**
     * è”系微信号
     */
    var ciContactsWx: String? = null
    /**
     * ä¼ çœŸ
     */
    var ciFax: String? = null
    /**
     * ç”µå­é‚®ç®±
     */
    var ciEmail: String? = null
    var ciExtension2: String? = null
    var ciExtension3: String? = null
    var ciRemark: String? = null
}
src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt
@@ -1,7 +1,9 @@
package com.flightfeather.uav.lightshare.bean
import com.fasterxml.jackson.annotation.JsonInclude
import com.flightfeather.uav.model.BaseMData
import com.flightfeather.uav.socket.bean.AirData
import com.flightfeather.uav.socket.eunm.FactorType
/**
 * @author riku
@@ -19,4 +21,14 @@
        var lng: Double? = null,
        //纬度
        var lat: Double? = null
)
) : BaseMData() {
        override fun getFactorData(type: FactorType): Double? {
                if (values == null) throw IllegalStateException(this.javaClass.name + ": ç›‘测数据数组为null")
                for (d in values!!) {
                        if (d.factorName == type.name) {
                                return d.factorData
                        }
                }
                return null
        }
}
src/main/kotlin/com/flightfeather/uav/model/BaseDataPrep.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.flightfeather.uav.model
/**
 * æ•°æ®é¢„处理模块
 * å¯¹äºŽ[BaseModel]进行模型分析时,对原数据需要进行异常值剔除及缺失数据补充等预处理操作,
 * ä½¿åŽŸå§‹æ•°æ®ç›¸å¯¹æ›´åŠ æŽ¥è¿‘çœŸå®žæƒ…å†µ
 */
abstract class BaseDataPrep<M : BaseMData, S : BaseSOP> {
    /**
     * ç›‘测数据预处理
     */
    abstract fun mDataPrep(mDataList: List<M>): List<M>
    /**
     * æ±¡æŸ“源预处理
     */
    abstract fun sopPrep(sopList: List<S>): List<S>
}
src/main/kotlin/com/flightfeather/uav/model/BaseEffect.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.flightfeather.uav.model
import com.flightfeather.uav.socket.eunm.FactorType
/**
 * å•个污染源对某条监测数据产生的影响结果
 */
class BaseEffect(
//    æ±¡æŸ“源标识
    val sourceId: String,
    val sourceName: String,
    val index: String
){
    //    å½±å“ç»“果值
    val value = mutableListOf<Pair<FactorType, Double>>()
    //    åˆ†ç±»æ ‡ç­¾
    val tag = mutableListOf<BaseTag>()
}
src/main/kotlin/com/flightfeather/uav/model/BaseMData.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.flightfeather.uav.model
import com.flightfeather.uav.socket.eunm.FactorType
/**
 * Monitoring data
 * ç›‘测数据 åŸºç±»
 */
abstract class BaseMData {
    abstract fun getFactorData(type: FactorType): Double?
}
src/main/kotlin/com/flightfeather/uav/model/BaseModel.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,216 @@
package com.flightfeather.uav.model
import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.common.utils.ExcelUtil
import com.flightfeather.uav.socket.eunm.FactorType
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook
import org.apache.poi.hssf.usermodel.HSSFWorkbook
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.math.round
/**
 * æ±¡æŸ“源影响程度权重分析模型
 * åŸºç±»
 */
abstract class BaseModel<M : BaseMData, S : BaseSOP> {
    abstract var dataPrep: BaseDataPrep<M, S>
    // æƒé‡å› å­ï¼Œåœ¨è¿›è¡Œè®¡ç®—分析时使用的监测因子
    abstract var factorTypes: List<FactorType>
    // æƒé‡å€¼ï¼Œå¤šç§æƒé‡è¿›è¡Œä¹˜ç§¯è®¡ç®—
    abstract var weights: List<BaseWeight<M, S>>
    // è®¡ç®—结果
    private val result = mutableSetOf<BaseEffect>()
    // ç»“果筛选方式
    abstract var sections: List<BaseSection<M, S>>
    /**
     * æ±¡æŸ“源影响程度计算
     * @param mDataList ç›‘测数据集合
     * @param sopList æ±¡æŸ“源集合
     */
    fun execute(mDataList: List<M>, sopList: List<S>) {
        result.clear()
        //1. æ•°æ®é¢„处理
        val mList = dataPrep.mDataPrep(mDataList)
        val sList = dataPrep.sopPrep(sopList)
        mList.forEach m@{ m ->
            if (!mDataCheck(m)) return@m
            sList.forEach s@{ s ->
                if (!sopCheck(s)) return@s
                weightCompute(m, s)
            }
        }
    }
    /**
     * å•点权重计算
     * è®¡ç®—污染源对单次监测数据产生的影响效果
     * @param mData ç›‘测数据
     * @param sop æ±¡æŸ“源
     */
    private fun weightCompute(mData: M, sop: S) {
        val effect = BaseEffect(sop.sourceId, sop.sourceName, sop.index)
        // å°†åŽŸç›‘æµ‹æ•°æ®æŒ‰ç…§æƒé‡è®¡ç®—å‡ºç»“æžœå€¼
        factorTypes.forEach { type ->
            var result = mData.getFactorData(type) ?: return@forEach
            weights.forEach {
                result *= it.getWeight(mData, sop)?: return
            }
            effect.value.add(Pair(type, result))
        }
        // å‘结果值添加筛选标签
        sections.forEach { it.filter(mData, sop, effect) }
        // ä¿å­˜ç»“æžœ
        result.add(effect)
    }
    fun outputToExcel(
        fName: String? = null,
        _workbook: HSSFWorkbook? = null,
        _out: FileOutputStream? = null,
        sheetName: String = "sheet1",
        done: Boolean = true
    ): Pair<HSSFWorkbook, FileOutputStream>? {
        val rMap = formatConversion()
        val workbook = _workbook ?: HSSFWorkbook()
        val fileName = fName ?: "污染溯源权重模型${DateUtil().DateToString(Date(), "yyyy-MM-ddHHmmss")}.xls"
//        val filePath = "E:\\work\\export\\$fileName"
//        val filePath = "E:\\工作\\开发\\走航监测\\算法相关\\自动输出\\$fileName"
        val filePath = "E:\\工作\\开发\\走航监测\\算法相关\\自动输出\\网格化\\$fileName"
        val out = _out ?: FileOutputStream(File(filePath))
        // è¡¨å¤´ç»„(多行表头)
        val heads = mutableListOf<Array<String>>()
        // è¡¨å¤´è¡Œ
        val h1 = mutableListOf<String>()
        h1.add("编号")
        h1.add("污染源")
//        arrayOf("早上", "上午", "中午", "下午", "傍晚", "夜间")
        val contents = mutableListOf<Array<Any>>()
        var isFirst = true
        rMap.forEach { (source, tMap) ->
            // æ–°å»ºä¸€è¡Œ
            val contentList = mutableListOf<Any>()
            // æ·»åŠ æ±¡æŸ“æºåç§°
            val l = source.split("(")
            val index = l[1].substring(0, l[1].lastIndex)
            contentList.add(index.toIntOrNull() ?: 0)
            contentList.add(l[0])
            tMap.forEach { (factorType, lMap) ->
                sections.forEach {
                    val types = mutableListOf<String>().apply {
                        addAll(it.sectionType)
                        addAll(it.constType)
                    }
                    var max = 0.0
                    var maxP = types[0]
                    types.forEach {se ->
                        val lKey = "$se($factorType)"
                        if (lMap.containsKey(lKey)) {
                            val dataList = lMap[lKey]
                            val size = dataList?.size
                            // æ·»åŠ è¯¥åˆ†ç±»ä½œä¸ºè¡¨å¤´
                            val h = lKey
//                            val h = "$lKey($size)"
                            if (!h1.contains(h)) {
                                h1.add(h)
                            }
                            // å°†åŽŸå§‹çš„æ•°æ®æ¢ç®—å¾—å‡ºç»“æžœï¼Œå¯ä»¥æ˜¯æ±‚å‡ºå‡å€¼ã€æ€»å’Œç­‰ç­‰ï¼ŒåŽç»­ä¿®æ”¹ä¸ºå¯ç”±ç”¨æˆ·è®¾å®š
                            // FIXME: 2021/6/23 æ­¤å¤„先默认为求均值
                            val average = dataList?.average()
                            if (average ?: 0.0 > max) {
                                max = average ?: 0.0
                                maxP = se
                            }
                            // å½“前行添加该分类下的结果值
                            contentList.add(average ?: 0.0)
                        }
                    }
                    if (isFirst) {
                        h1.add("最高时段")
                        h1.add("最高时段值")
                    }
                    contentList.add(maxP)
                    contentList.add(max)
                }
            }
            isFirst = false
            contents.add(contentList.toTypedArray())
        }
        contents.sortByDescending {
            val i = it[0]
            if (i is Int) {
                i
            } else {
                0
            }
        }
        heads.add(h1.toTypedArray())
        ExcelUtil.write(heads, contents, workbook, sheetName)
        return if (!done) {
            Pair(workbook, out)
        } else {
            workbook.write(out)
            workbook.close()
            out.flush()
            out.close()
            null
        }
    }
    private fun formatConversion(): Map<String, Map<String, Map<String?, MutableList<Double>>>> {
        val rMap = mutableMapOf<String, MutableMap<String, MutableMap<String?, MutableList<Double>>>>()
        println("结果长度:${result.size}")
        result.forEach { e ->
            val rKey = "${e.sourceName}(${e.index})"
            if (!rMap.containsKey(rKey)) {
                rMap[rKey] = mutableMapOf()
            }
            val lMap = rMap[rKey]!!
            e.value.forEach { v ->
                if (!lMap.containsKey(v.first.des)) {
                    lMap[v.first.des] = mutableMapOf()
                }
                val tMap = lMap[v.first.des]!!
                e.tag.forEach { t ->
                    val factorName = v.first.des
                    val lKey = t.levelName + "($factorName)"
                    if (!tMap.containsKey(lKey)) {
                        tMap[lKey] = mutableListOf()
                    }
                    tMap[lKey]?.add(v.second)
                }
            }
        }
        return rMap
    }
    /**
     * ç›‘测数据合法性检查
     */
    abstract fun mDataCheck(m: M): Boolean
    /**
     * æ±¡æŸ“源数据合法性检查
     */
    abstract fun sopCheck(s: S): Boolean
}
src/main/kotlin/com/flightfeather/uav/model/BaseSOP.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.flightfeather.uav.model
/**
 * Source of pollution
 * æ±¡æŸ“源 åŸºç±»
 */
open class BaseSOP(
    // æ±¡æŸ“源标识
    var sourceId: String,
    var sourceName: String,
    var index: String
)
src/main/kotlin/com/flightfeather/uav/model/BaseSection.kt
@@ -1,9 +1,55 @@
package com.flightfeather.uav.model
import com.flightfeather.uav.lightshare.bean.DataVo
/**
 * ç»Ÿè®¡åˆ†æ®µæˆªé¢
 * ç»Ÿè®¡åˆ†ç±»
 * åŒä¸€ç±»åž‹çš„监测因子按照某一属性进行分段综合统计,输出统计结果,
 * å¸¸è§çš„如按照时间进行分段统计
 */
abstract class BaseSection {
abstract class BaseSection<M : BaseMData, S : BaseSOP> {
    // åŒºé—´é˜ˆå€¼
    abstract val sectionValues: List<Double>
    // åŒºé—´å¯¹åº”类型
    abstract val sectionType: List<String>
    // å¸¸é©»æ ‡ç­¾
    open val constType = listOf<String>()
    abstract val tagClz: Class<out BaseTag>
    fun filter(mData: M, sop: S, effect: BaseEffect) {
        val v = onSectionValue(mData, sop, effect)
        val type = sectionCal(v)
        val tag = tagClz.newInstance()
        tag.level = type.first
        tag.levelName = type.second
        effect.tag.add(tag)
        var level = sectionType.size
        constType.forEach {
            val cTag = tagClz.newInstance()
            cTag.level = level
            cTag.levelName = it
            effect.tag.add(cTag)
            level++
        }
    }
    abstract fun onSectionValue(mData: M, sop: S, effect: BaseEffect): Double
    /**
     * ç±»åž‹ç­›é€‰
     * @param value å½±å“å› ç´ çš„值
     */
    private fun sectionCal(value: Double): Pair<Int, String> {
        for (i in sectionValues.indices) {
            if (value < sectionValues[i]) {
                return Pair(i, sectionType[i])
            }
        }
        return Pair(sectionType.lastIndex, sectionType.last())
    }
}
src/main/kotlin/com/flightfeather/uav/model/BaseTag.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.flightfeather.uav.model
/**
 * æ±¡æŸ“源影响结果筛选类型
 */
abstract class BaseTag {
    // ç­›é€‰ç±»åž‹åç§°
    abstract var name: String
    // å½“前筛选级别
    var level: Int = 0
    // å½“前筛选级别名称
    var levelName: String? = null
}
src/main/kotlin/com/flightfeather/uav/model/BaseWeight.kt
@@ -1,21 +1,53 @@
package com.flightfeather.uav.model
import com.flightfeather.uav.lightshare.bean.DataVo
/**
 * æƒé‡
 * æŸç§å½±å“å› ç´ åœ¨ä¸åŒæƒ…况下对某种监测数据产生的权重影响
 */
abstract class BaseWeight {
abstract class BaseWeight <M: BaseMData, S : BaseSOP> {
    abstract val tag:String
//    åŒºé—´é˜ˆå€¼
    abstract val sectionValues: List<Double>
//    åŒºé—´å¯¹åº”权重
    abstract val weights: List<Double>
    // ä¸Šæ¬¡è®¡ç®—的权重值
    private var lastWeight = -1.0
    // æƒé‡æ˜¯å¦å˜åŒ–
    var isChange = true
    fun getWeight(mData: M, sop: S): Double? {
        if (!isChange) {
            if (lastWeight < 0) {
                val v = onWeightFactor(mData, sop) ?: return null
                lastWeight = weightCal(v)
            }
//            println("$tag: $lastWeight")
            return lastWeight
        } else {
            val v = onWeightFactor(mData, sop) ?: return null
            lastWeight = weightCal(v)
//            println("$tag: $lastWeight")
            return lastWeight
        }
    }
    /**
     * èŽ·å–æƒé‡å› å­å€¼
     */
    abstract fun onWeightFactor(mData: M, sop: S): Double?
    /**
     * æƒé‡è®¡ç®—
     * @param value å½±å“å› ç´ çš„值
     */
    fun weightCal(value: Double): Double {
    private fun weightCal(value: Double): Double {
        for (i in sectionValues.indices) {
            if (value < sectionValues[i]) {
                return weights[i]
src/main/kotlin/com/flightfeather/uav/model/TimeTag.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.flightfeather.uav.model
/**
 * æ—¶æ®µæ ‡ç­¾
 * [6,9,12,14,17,20]; [6,9)为早上,之后依次为上午,中午,下午,傍晚和晚上
 */
class TimeTag :BaseTag() {
    override var name: String = "时段"
}
src/main/kotlin/com/flightfeather/uav/model/epw/EPWDataPrep.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,138 @@
package com.flightfeather.uav.model.epw
import com.flightfeather.uav.lightshare.bean.CompanySOP
import com.flightfeather.uav.lightshare.bean.DataVo
import com.flightfeather.uav.model.BaseDataPrep
import com.flightfeather.uav.socket.eunm.FactorType
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
class EPWDataPrep : BaseDataPrep<DataVo, CompanySOP>() {
    // å‘前检索的数据记录数
    private val ncal = 15
    // æ ‡å‡†å·®å€æ•°å‚æ•°
    private val nstd = 3
    // å‡å€¼å€æ•°å‚æ•°
    private val xratio = 3
    // éœ€è¦å¤„理的因子类型
    private val calTypes =
//        emptyList<String>()
        WeightType.prep
    override fun mDataPrep(mDataList: List<DataVo>): List<DataVo> {
        mDataList.forEach {
            it.values?.forEach v@{a ->
                if (!calTypes.contains(a.factorName)) return@v
                val range = FactorType.getRange(a.factorName) ?: return@v
                // åˆ¤æ–­æ•°æ®æ˜¯å¦åœ¨åˆç†èŒƒå›´å†…
                if (a.factorData ?: 0.0 < range.first || a.factorData ?: 0.0 > range.second) {
                    a.factorData = null
                }
            }
        }
//        val newDataList = mutableListOf<DataVo>()
//        mDataList.forEach {
//            newDataList.add(it.copy())
//        }
        var i = ncal
        while (i < mDataList.size) {
            for (y in mDataList[i].values?.indices ?: 0..0) {
                val it = mDataList[i].values?.get(y) ?: continue
                if (!calTypes.contains(it.factorName)) continue
                val vMax = FactorType.getVMax(it.factorName) ?: continue
                it.factorData ?: continue
                if (it.factorData!! > vMax) {
                    val list = mDataList.subList(i - ncal, i)
                    // åŽ»é™¤æ— æ•ˆå€¼çš„å¹³å‡
                    val avg = average(list, it.factorName)
                    // åŽ»é™¤æ— æ•ˆå€¼çš„æ ‡å‡†å·®
                    val std = standardDeviation(avg.first, list, it.factorName)
                    // åˆç†æœ€å¤§å€¼
                    val max = max(avg.first + std * nstd, avg.first + avg.first * xratio)
                    // åˆç†æœ€å°å€¼
                    val min = min(avg.first - std * nstd, avg.first / (1 + xratio))
                    // æ•°æ®ä¸å¤„于合理范围并且有效个数达标时,采用计算所得均值代替原始值
                    if (avg.second > max(ncal / 5, 2)
                        && (it.factorData!! < min || it.factorData!! > max)
                    ) {
                        // åŽŸå§‹æ•°æ®
                        it.factorData = null
//                        newDataList[i].values?.get(y)?.factorData = avg.first
                    }
                }
            }
            i++
        }
        return mDataList
    }
    override fun sopPrep(sopList: List<CompanySOP>): List<CompanySOP> {
        return sopList
    }
    /**
     * åŽ»é™¤æ— æ•ˆå€¼çš„å¹³å‡
     * @param list ç›‘测数据
     * @return å‡å€¼å’Œæœ‰æ•ˆæ•°æ®ä¸ªæ•°
     */
    private fun average(list: List<DataVo>, factorName:String?): Pair<Double, Int> {
        var t = 0.0
        var c = 0
        list.forEach {
            for (i in it.values?.indices ?: 0..0) {
                val f = it.values?.get(i)
                if (f?.factorName == factorName) {
                    if (f?.factorData != null) {
                        t += f.factorData!!
                        c++
                    }
                    break
                }
            }
        }
        val avg = if (c == 0) {
            0.0
        } else {
            t / c
        }
        return Pair(avg, c)
    }
    /**
     * åŽ»é™¤æ— æ•ˆå€¼çš„æ ‡å‡†å·®
     */
    private fun standardDeviation(avg: Double, list: List<DataVo>, factorName: String?): Double {
        var t = 0.0
        var c = 0
        list.forEach {
            for (i in it.values?.indices ?: 0..0) {
                val f = it.values?.get(i)
                if (f?.factorName == factorName) {
                    if (f?.factorData != null) {
                        t += (f.factorData!! - avg) * (f.factorData!! - avg)
                        c++
                    }
                    break
                }
            }
        }
        return if (c <= 1) {
            0.0
        } else {
            sqrt(t / (c - 1))
        }
    }
}
src/main/kotlin/com/flightfeather/uav/model/epw/EPWModel.kt
@@ -1,7 +1,9 @@
package com.flightfeather.uav.model.epw
import com.flightfeather.uav.domain.entity.Company
import com.flightfeather.uav.lightshare.bean.CompanySOP
import com.flightfeather.uav.lightshare.bean.DataVo
import com.flightfeather.uav.model.*
import com.flightfeather.uav.socket.eunm.FactorType
import java.math.BigDecimal
/**
@@ -9,41 +11,19 @@
 * æ ¹æ®èµ°èˆªç›‘测数据,结合风速、风向、监测点与企业的相对位置等因素,计算企业对监测区域的影响程度
 * @author riku
 */
class EPWModel {
class EPWModel : BaseModel<DataVo, CompanySOP>() {
    private val windDirWeight = WindDirWeight()
    private val windDisWeight = WindDisWeight()
    override var dataPrep: BaseDataPrep<DataVo, CompanySOP> = EPWDataPrep()
    private lateinit var datas: List<DataVo>
    private lateinit var sources: List<Company>
    override var factorTypes: List<FactorType> = WeightType.weightType
    fun execute() {
        datas.forEach d@{d ->
            if (d.lng == null || d.lng == 0.0 || d.lat == null || d.lat == 0.0) {
                return@d
            }
    override var weights: List<BaseWeight<DataVo, CompanySOP>> = listOf(WindDirWeight(), WindDisWeight())
            var con = 0
            var ws = 0.0
            var wd = 0
            var hr = 0
            val lng = d.lng
            val lat = d.lat
    override var sections: List<BaseSection<DataVo, CompanySOP>> = listOf(TimeSection())
            sources.forEach s@ { s ->
                // ç»çº¬åº¦æœ‰æ•ˆæ€§åˆ¤æ–­
                if (s.ciLongitude == null || s.ciLongitude == BigDecimal(0) || s.ciLatitude == null || s.ciLatitude == BigDecimal(0)) {
                    return@s
                }
    override fun mDataCheck(m: DataVo): Boolean = !(m.lng == null || m.lng == 0.0 || m.lat == null || m.lat == 0.0)
                val p1 = Pair(lng!!, lat!!)
                val p2 = Pair(s.ciLongitude.toDouble(), s.ciLatitude.toDouble())
                windDirWeight.getWeight(p1, p2, wd)
    override fun sopCheck(s: CompanySOP): Boolean =
        !(s.ciLongitude == null || s.ciLongitude == BigDecimal(0) || s.ciLatitude == null || s.ciLatitude == BigDecimal(0))
                windDisWeight.getWeight(p1, p2, ws)
            }
        }
    }
}
src/main/kotlin/com/flightfeather/uav/model/epw/TimeSection.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.flightfeather.uav.model.epw
import com.flightfeather.uav.lightshare.bean.CompanySOP
import com.flightfeather.uav.lightshare.bean.DataVo
import com.flightfeather.uav.model.BaseEffect
import com.flightfeather.uav.model.BaseSection
import com.flightfeather.uav.model.BaseTag
import com.flightfeather.uav.model.TimeTag
/**
 * æ—¶æ®µåˆ†ç±»ç»Ÿè®¡
 * [6,9,12,14,17,20]; [6,9)为早上,之后依次为上午,中午,下午,傍晚和晚上
 */
class TimeSection : BaseSection<DataVo, CompanySOP>() {
    override val sectionValues: List<Double> = listOf(6.0, 9.0, 12.0, 14.0, 17.0, 20.0)
    override val sectionType: List<String> = listOf("凌晨", "早上", "上午", "中午", "下午", "傍晚", "夜间")
    override val tagClz: Class<out BaseTag> = TimeTag::class.java
    override val constType: List<String> = listOf("综合")
    override fun onSectionValue(mData: DataVo, sop: CompanySOP, effect: BaseEffect): Double {
        return getHour(mData.time!!)
    }
    private fun getHour(time: String): Double {
        return if (time.length >= 13) {
            time.substring(11, 13).toDoubleOrNull() ?: 0.0
        } else {
            0.0
        }
    }
}
src/main/kotlin/com/flightfeather/uav/model/epw/WeightType.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.flightfeather.uav.model.epw
import com.flightfeather.uav.socket.eunm.FactorType
object WeightType {
    val prep = listOf(
        FactorType.NO2.des,
        FactorType.CO.des,
        FactorType.H2S.des,
        FactorType.SO2.des,
        FactorType.O3.des,
        FactorType.PM25.des,
        FactorType.PM10.des,
        FactorType.VOC.des,
        FactorType.WIND_SPEED.des
    )
    val weightType = listOf(
        FactorType.NO2,
        FactorType.CO,
        FactorType.H2S,
        FactorType.SO2,
        FactorType.O3,
        FactorType.PM25,
        FactorType.PM10,
        FactorType.VOC,
    )
}
src/main/kotlin/com/flightfeather/uav/model/epw/WindDirWeight.kt
@@ -1,23 +1,33 @@
package com.flightfeather.uav.model.epw
import com.flightfeather.uav.lightshare.bean.CompanySOP
import com.flightfeather.uav.lightshare.bean.DataVo
import com.flightfeather.uav.model.BaseWeight
import com.flightfeather.uav.socket.eunm.FactorType
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.sqrt
/**
 *
 * é£Žå‘权重
 */
class WindDirWeight : BaseWeight() {
class WindDirWeight : BaseWeight<DataVo, CompanySOP>() {
    override val tag: String = "风向权重"
    override val sectionValues: List<Double> = listOf(22.5, 67.5, 112.5, 157.5, 180.0)
    override val weights: List<Double> = listOf(1.0, 0.8, 0.5, 0.2, 0.1)
    fun getWeight(deg: Double):Double {
        return weightCal(deg)
    override fun onWeightFactor(mData: DataVo, sop: CompanySOP): Double? {
        val p1 = Pair(mData.lng!!, mData.lat!!)
        val p2 = Pair(sop.ciLongitude!!.toDouble(), sop.ciLatitude!!.toDouble())
        val wd = mData.getFactorData(FactorType.WIND_DIRECTION) ?: 0.0
        return getAngle(p1, p2, wd)
    }
    fun getWeight(p1: Pair<Double, Double>, p2: Pair<Double, Double>, wd: Int): Double {
    private fun getAngle(p1: Pair<Double, Double>, p2: Pair<Double, Double>, wd: Double): Double {
        val dx = p2.first - p1.first
        val dy = p2.second - p1.second
        var x1 = atan2(dy, dx) * 180 / PI
@@ -26,7 +36,7 @@
        if (x2 < 0) x2 += 360
        x1 = abs(x2 - x1)
        if (x1>180) x1 = 360 - x1
        return weightCal(x1)
//        println("夹角:$x1")
        return x1
    }
}
src/main/kotlin/com/flightfeather/uav/model/epw/WindDisWeight.kt
@@ -1,27 +1,39 @@
package com.flightfeather.uav.model.epw
import com.flightfeather.uav.lightshare.bean.CompanySOP
import com.flightfeather.uav.lightshare.bean.DataVo
import com.flightfeather.uav.model.BaseWeight
import com.flightfeather.uav.socket.eunm.FactorType
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.sqrt
/**
 * é£Žé€Ÿè·ç¦»æƒé‡
 * ç›‘测点与污染源之间的物理距离与当前风速得出的权重
 */
class WindDisWeight : BaseWeight() {
class WindDisWeight : BaseWeight<DataVo, CompanySOP>() {
    override val tag: String = "风速距离权重"
    override val sectionValues: List<Double> = listOf(2.0, 5.0, 8.0, 12.0, 20.0, 30.0)
    override val weights: List<Double> = listOf(1.0, 0.8, 0.6, 0.5, 0.3, 0.0)
    fun getWeight(dis: Double, ws: Double): Double {
        val value = dis / 1000 / ws / 60
        return weightCal(value)
    override fun onWeightFactor(mData: DataVo, sop: CompanySOP): Double? {
        val p1 = Pair(mData.lng!!, mData.lat!!)
        val p2 = Pair(sop.ciLongitude!!.toDouble(), sop.ciLatitude!!.toDouble())
        val ws = mData.getFactorData(FactorType.WIND_SPEED)
        return if (ws == null) null else getWindSpeed(p1, p2, ws)
    }
    fun getWeight(p1: Pair<Double, Double>, p2: Pair<Double, Double>, ws: Double): Double {
    private fun getWindSpeed(p1: Pair<Double, Double>, p2: Pair<Double, Double>, ws: Double): Double {
        val dx = p2.first - p1.first
        val dy = p2.second - p1.second
        val dis = sqrt(abs(dx * dx) + abs(dy * dy)) * 100
        return getWeight(dis, ws)
//        println("距离:$dis")
        val min = dis * 1000 / max(ws, 1.0) / 60
//        println("距离转为风速分钟数:$min")
        return min
    }
}
src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt
@@ -47,5 +47,91 @@
            HEIGHT.value -> HEIGHT
            else -> null
        }
        fun getByName(name: String?): FactorType? = when (name) {
            NO2.des -> NO2
            CO.des -> CO
            H2S.des -> H2S
            SO2.des -> SO2
            O3.des -> O3
            PM25.des -> PM25
            PM10.des -> PM10
            TEMPERATURE.des -> TEMPERATURE
            HUMIDITY.des -> HUMIDITY
            VOC.des -> VOC
            NOI.des -> NOI
            LNG.des -> LNG
            LAT.des -> LAT
            VELOCITY.des -> VELOCITY
            TIME.des -> TIME
            WIND_SPEED.des -> WIND_SPEED
            WIND_DIRECTION.des -> WIND_DIRECTION
            HEIGHT.des -> HEIGHT
            else -> null
        }
        fun getRange(name: String?): Pair<Double, Double>? {
            getByName(name)?.let {
                return getRange(it)
            }
            return null
        }
        /**
         * èŽ·å–ç›‘æµ‹å› å­çš„åˆç†èŒƒå›´
         */
        fun getRange(type: FactorType): Pair<Double, Double>? = when (type) {
            NO2 -> Pair(0.1, 1000.0)
            CO -> Pair(1.0, 5000.0)
            H2S -> Pair(0.1, 1000.0)
            SO2 -> Pair(0.1, 1000.0)
            O3 -> Pair(0.1, 2000.0)
            PM25 -> Pair(0.1, 5000.0)
            PM10 -> Pair(0.1, 10000.0)
            TEMPERATURE -> Pair(-20.0, 70.0)
            HUMIDITY -> Pair(0.0, 110.0)
            VOC -> Pair(1.0, 5000.0)
            NOI -> Pair(0.1, 1000.0)
            LNG -> Pair(0.0, 180.0)
            LAT -> Pair(0.0, 90.0)
            VELOCITY -> Pair(0.0, 500.0)
            TIME -> null
            WIND_SPEED -> Pair(0.0, 100.0)
            WIND_DIRECTION -> Pair(0.0, 360.0)
            HEIGHT -> Pair(0.0, 1000.0)
            else -> null
        }
        fun getVMax(name: String?): Double? {
            getByName(name)?.let {
                return getVMax(it)
            }
            return null
        }
        /**
         * ä¸å¤„理低于此值的值
         */
        fun getVMax(type: FactorType): Double? = when (type) {
            NO2 -> 10.0
            CO -> 100.0
            H2S -> 10.0
            SO2 -> 10.0
            O3 -> 10.0
            PM25 -> 10.0
            PM10 -> 10.0
            TEMPERATURE -> 10.0
            HUMIDITY -> 10.0
            VOC -> 10.0
            NOI -> 10.0
            LNG -> 0.0
            LAT -> 0.0
            VELOCITY -> 0.0
            TIME -> 0.0
            WIND_SPEED -> 2.0
            WIND_DIRECTION -> 0.0
            HEIGHT -> 0.0
            else -> null
        }
    }
}
src/main/resources/application.yml
@@ -1,17 +1,25 @@
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
#    å±€åŸŸç½‘服务器
#    url: jdbc:mysql://192.168.0.200:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
#    username: root
#    password: cn.FLIGHTFEATHER
    url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: dronemonitor
    password: dronemonitor_hackxrnomxm
#    çº¿ä¸ŠæœåС噍
#    url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
#    username: dronemonitor
#    password: dronemonitor_hackxrnomxm
#    å¼€å‘本地服务器
#    url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
#    username: root
#    password: 123456
#   å¼€å‘远程服务器
    url: jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: remoteU1
    password: eSoF8DnzfGTlhAjE
    hikari:
      maximum-pool-size: 500
      minimum-idle: 20
src/main/resources/log4j2.xml
@@ -1,67 +1,67 @@
<!--<?xml version="1.0" encoding="UTF-8"?>-->
<!--&lt;!&ndash;-->
    <!--6个优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。-->
    <!--如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出-->
    <!--设置为OFF è¡¨ç¤ºä¸è®°å½•log4j2本身的日志,-->
 <!--&ndash;&gt;-->
<?xml version="1.0" encoding="UTF-8"?>
<!--
    6个优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
    å¦‚果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出
    è®¾ç½®ä¸ºOFF è¡¨ç¤ºä¸è®°å½•log4j2本身的日志,
 -->
<!--&lt;!&ndash; status:用来指定log4j本身的打印日志级别,monitorInterval:指定log4j自动重新配置的监测间隔时间 &ndash;&gt;-->
<!--<configuration status="INFO" monitorInterval="30">-->
    <!--&lt;!&ndash; è‡ªå·±è®¾ç½®å±žæ€§ï¼ŒåŽé¢é€šè¿‡${}来访问 &ndash;&gt;-->
    <!--<properties>-->
        <!--<property name="LOG_HOME">../obdLogs</property>-->
    <!--</properties>-->
<!-- status:用来指定log4j本身的打印日志级别,monitorInterval:指定log4j自动重新配置的监测间隔时间 -->
<configuration status="INFO" monitorInterval="30">
    <!-- è‡ªå·±è®¾ç½®å±žæ€§ï¼ŒåŽé¢é€šè¿‡${}来访问 -->
    <properties>
        <property name="LOG_HOME">../uavLogs</property>
    </properties>
    <!--<appenders>-->
        <!--&lt;!&ndash;Appender 1. è¾“出到Console控制台,指定输出格式和过滤器等级为INFO &ndash;&gt;-->
        <!--<Console name="Console" target="SYSTEM_OUT">-->
            <!--&lt;!&ndash;ThresholdFilter指定日志消息的输出最低层次&ndash;&gt;-->
            <!--<ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>-->
            <!--<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>-->
        <!--</Console>-->
    <appenders>
        <!--Appender 1. è¾“出到Console控制台,指定输出格式和过滤器等级为INFO -->
        <Console name="Console" target="SYSTEM_OUT">
            <!--ThresholdFilter指定日志消息的输出最低层次-->
            <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </Console>
        <!--&lt;!&ndash;Appender 2. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志&ndash;&gt;-->
        <!--<File name="allLog" fileName="${LOG_HOME}/all.log">-->
            <!--<ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>-->
            <!--<PatternLayout charset="UTF-8" pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>-->
        <!--</File>-->
        <!--Appender 2. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志-->
        <File name="allLog" fileName="${LOG_HOME}/all.log">
            <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>
        <!--&lt;!&ndash;Appender 3. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志&ndash;&gt;-->
        <!--<RollingFile name="debugLog" fileName="${LOG_HOME}/debug.log" filePattern="${LOG_HOME}/debug-%i.log">-->
            <!--<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>-->
            <!--<PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%F:%L] - %m%n"/>-->
            <!--<SizeBasedTriggeringPolicy size="3KB"/>-->
            <!--&lt;!&ndash; DefaultRolloverStrategy ä¸­çš„参数max,可以限制 SizeBasedTriggeringPolicy中size超出后,只保留max个存档&ndash;&gt;-->
            <!--<DefaultRolloverStrategy max="10"/>-->
        <!--</RollingFile>-->
        <!--Appender 3. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志-->
        <RollingFile name="debugLog" fileName="${LOG_HOME}/debug.log" filePattern="${LOG_HOME}/debug-%i.log">
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%F:%L] - %m%n"/>
            <SizeBasedTriggeringPolicy size="3KB"/>
            <!-- DefaultRolloverStrategy ä¸­çš„参数max,可以限制 SizeBasedTriggeringPolicy中size超出后,只保留max个存档-->
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
        <!--&lt;!&ndash;Appender 4. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是每分钟第一次的日志事件。ERROR日志是按分钟产生日志 &ndash;&gt;-->
        <!--<RollingFile name="errorLog" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/error-%d{yyyy-MM-dd_HH-mm}.log">-->
            <!--<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>-->
            <!--<PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%C:%F:%L] - %m%n"/>-->
            <!--<TimeBasedTriggeringPolicy/>-->
        <!--</RollingFile>-->
        <!--Appender 4. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是每分钟第一次的日志事件。ERROR日志是按分钟产生日志 -->
        <RollingFile name="errorLog" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/error-%d{yyyy-MM-dd_HH-mm}.log">
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%C:%F:%L] - %m%n"/>
            <TimeBasedTriggeringPolicy/>
        </RollingFile>
        <!--<RollingFile name="RollingFile" fileName="${LOG_HOME}/rar.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{MM-dd-yyyy}-%i.log.gz">-->
            <!--<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>-->
            <!--&lt;!&ndash;日志文件最大值 ç¬¬äºŒå¤©åŽ‹ç¼©&ndash;&gt;-->
            <!--<Policies>-->
                <!--<TimeBasedTriggeringPolicy/>-->
                <!--<SizeBasedTriggeringPolicy size="10 MB"/>-->
            <!--</Policies>-->
        <!--</RollingFile>-->
        <RollingFile name="RollingFile" fileName="${LOG_HOME}/rar.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <!--日志文件最大值 ç¬¬äºŒå¤©åŽ‹ç¼©-->
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
        </RollingFile>
    <!--</appenders>-->
    <!--&lt;!&ndash;root é»˜è®¤åŠ è½½&ndash;&gt;-->
    <!--<loggers>-->
        <!--<root level="INFO">-->
    </appenders>
    <!--root é»˜è®¤åŠ è½½-->
    <loggers>
        <root level="INFO">
            <!--<appender-ref ref="Console"/>-->
            <!--&lt;!&ndash;<appender-ref ref="allLog"/>&ndash;&gt;-->
<!--            <appender-ref ref="allLog"/>-->
            <!--<appender-ref ref="debugLog"/>-->
            <!--<appender-ref ref="errorLog"/>-->
            <!--<appender-ref ref="RollingFile"/>-->
        <!--</root>-->
    <!--</loggers>-->
<!--</configuration>-->
        </root>
    </loggers>
</configuration>
src/test/kotlin/com/flightfeather/uav/Test.kt
@@ -1,6 +1,7 @@
package com.flightfeather.uav
import com.flightfeather.uav.common.utils.FileExchange
import com.flightfeather.uav.domain.entity.Company
import com.flightfeather.uav.socket.bean.DataUnit
import com.flightfeather.uav.socket.decoder.AirDataDecoder
import com.flightfeather.uav.socket.eunm.AirCommandUnit
@@ -47,4 +48,22 @@
    fun dataChange() {
        FileExchange().doTask2()
    }
    @Test
    fun listCopy() {
        val l1 = listOf(Company().apply { ciGuid = "a" }, Company().apply { ciGuid = "b" }, Company().apply { ciGuid = "c" })
        val l2 = mutableListOf<Company>().apply { addAll(l1) }
        l2[1].ciGuid = "d"
        println(l1)
        println(l2)
    }
    @Test
    fun average() {
        for (i in 0..2) {
            println(i)
        }
        val list = listOf(1, 2, 3, 4, 5, 0)
        println(list.average())
    }
}
src/test/kotlin/com/flightfeather/uav/model/epw/EPWModelTest.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,117 @@
package com.flightfeather.uav.model.epw
import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.domain.entity.Company
import com.flightfeather.uav.domain.mapper.CompanyMapper
import com.flightfeather.uav.lightshare.bean.CompanySOP
import com.flightfeather.uav.lightshare.bean.DataVo
import com.flightfeather.uav.lightshare.service.RealTimeDataService
import org.apache.poi.hssf.usermodel.HSSFWorkbook
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
import tk.mybatis.mapper.entity.Example
import java.io.FileOutputStream
import java.util.*
@RunWith(SpringRunner::class)
@SpringBootTest
class EPWModelTest {
    @Autowired
    lateinit var realTimeDataService: RealTimeDataService
    @Autowired
    lateinit var companyMapper: CompanyMapper
    @Test
    fun test1() {
        val company = companyMapper
            .selectAll()
//            .selectByExample(Example(Company::class.java).apply {
//            createCriteria()
//                .orEqualTo("ciGuid", "Dp5iDYfKY7qi04Ze")
//                .orEqualTo("ciGuid", "C9ZzWbc49AHuxa0U")
//                .orEqualTo("ciGuid", "15xCtnjxa9pfmDSj")
//                .orEqualTo("ciGuid", "mbd8mRmpRtcUsT61")
//                .orEqualTo("ciGuid", "xh7fpulnFQDQjb3e")
//                .orEqualTo("ciGuid", "mXef1NeDZyguGDLN")
//                .orEqualTo("ciGuid", "LEU330ksO54BEH8A")
//                .orEqualTo("ciGuid", "aZpHXOktTEuzUcTX")
//                .orEqualTo("ciGuid", "dNDMoJfresNn5hPU")
//                .orEqualTo("ciGuid", "J3euwNl19WZvH7iE")
//                .orEqualTo("ciGuid", "eb2kbFjhCZN2s9If")
//                .orEqualTo("ciGuid", "0PjZXFkpp1KT6hEM")
//                .orEqualTo("ciGuid", "ymXoupcbVMa1bBF1")
//        })
        val companySOPList = mutableListOf<CompanySOP>()
        company.forEach {
            val companySOP = CompanySOP(it.ciGuid, it.ciName, it.ciExtension1)
            BeanUtils.copyProperties(it, companySOP)
            companySOPList.add(companySOP)
        }
        val dataSet = mutableListOf<DataVo>()
        val timeSet = listOf(
            Pair("2021-06-18 15:00:00", "2021-06-18 23:59:59"),
            Pair("2021-06-19 00:00:00", "2021-06-19 23:59:59"),
            Pair("2021-06-20 00:00:00", "2021-06-20 23:59:59"),
            Pair("2021-06-21 00:00:00", "2021-06-21 23:59:59"),
            Pair("2021-06-22 00:00:00", "2021-06-22 23:59:59"),
            Pair("2021-06-23 00:00:00", "2021-06-23 23:59:59"),
            Pair("2021-06-24 00:00:00", "2021-06-24 23:59:59"),
            Pair("2021-06-25 00:00:00", "2021-06-25 23:59:59"),
            Pair("2021-06-26 00:00:00", "2021-06-26 23:59:59"),
            Pair("2021-06-27 00:00:00", "2021-06-27 23:59:59"),
            Pair("2021-06-28 00:00:00", "2021-06-28 23:59:59"),
            Pair("2021-06-29 00:00:00", "2021-06-29 23:59:59"),
            Pair("2021-06-30 00:00:00", "2021-06-30 08:00:00"),
//            Pair("2021-03-26 11:28:12", "2021-03-26 21:30:00"),
//            Pair("2021-04-09 07:18:12", "2021-04-09 22:04:39"),
//            Pair("2021-04-10 08:00:02", "2021-04-10 09:44:18"),
//            Pair("2021-04-21 16:46:12", "2021-04-21 21:18:35"),
//            Pair("2021-05-24 11:10:12", "2021-05-24 19:31:02"),
//            Pair("2021-06-04 09:02:40", "2021-06-04 20:14:18"),
        )
        val epwModel = EPWModel()
        var workbook: HSSFWorkbook? = null
        var out: FileOutputStream? = null
        for (i in timeSet.indices) {
            val it = timeSet[i]
            val dataList =
                realTimeDataService.getSecondData("0d0000000001", it.first, it.second, 1, 100000).data ?: emptyList()
            dataList.forEach {
                if (it.lng == 0.0 && it.lat == 0.0) {
                    it.lng = 121.235813
                    it.lat = 30.835898
                }
            }
            dataSet.addAll(dataList)
            println()
            println("[${it.first}]数据量: ${dataList.size}")
            epwModel.execute(dataList, companySOPList)
            val p = epwModel.outputToExcel(
//                "污染权重分析结果-综合-${DateUtil().DateToString(Date(), "yyyy-MM-ddHHmmss")}.xls",
//                "污染权重分析结果-${it.first.substring(0, 10)}.xls",
                "污染权重分析结果-网格化-${it.first.substring(0, 10)}.xls",
                workbook,
                out,
                it.first.substring(0, 10),
//                false
//                i == timeSet.size - 1
            )
//            p?.first?.let { workbook = it }
//            p?.second?.let { out = it }
        }
        println(dataSet.size)
        epwModel.execute(dataSet, companySOPList)
        epwModel.outputToExcel("污染权重分析结果-综合-${DateUtil().DateToString(Date(), "yyyy-MM-ddHHmmss")}.xls", workbook, out, "综合")
    }
}