MapLibre Kotlin版日期过滤实现问题求助
问题:OpenHistoricalMap日期过滤迁移到Kotlin后失效
尝试将OpenHistoricalMap的MapLibre JS日期过滤功能迁移到Kotlin平台后,出现两种异常:要么过滤完全不生效,要么部分国家名称无法被移除。以下是问题代码:
@HiltViewModel class MapViewModel @Inject constructor( private val settingsDataStore: SettingsDataStore ) : ViewModel() { val dateRange = settingsDataStore.dateRange fun filterLayersByDate(map: MapLibreMap, date: Date) { val dateRange = DateRange.fromDate(date) val style = map.style for (layer in style?.layers!!) { when (layer) { is LineLayer -> { layer.setFilter( constrainExpressionFilterByDateRange(layer.filter, dateRange) ) } is FillLayer -> { layer.setFilter( constrainExpressionFilterByDateRange(layer.filter, dateRange) ) } is CircleLayer -> { layer.setFilter( constrainExpressionFilterByDateRange(layer.filter, dateRange) ) } is SymbolLayer -> { layer.setFilter( constrainExpressionFilterByDateRange(layer.filter, dateRange) ) } is HeatmapLayer -> { layer.setFilter( constrainExpressionFilterByDateRange(layer.filter, dateRange) ) } is FillExtrusionLayer -> { layer.setFilter( constrainExpressionFilterByDateRange(layer.filter, dateRange) ) } else -> { } } } } private fun constrainExpressionFilterByDateRange( filter: Expression? = null, dateRange: DateRange, variablePrefix: String = "maplibre_gl_dates" ): Expression { val startDecimalYearVariable = "${variablePrefix}__startDecimalYear" val startISODateVariable = "${variablePrefix}__startISODate" val endDecimalYearVariable = "${variablePrefix}__endDecimalYear" val endISODateVariable = "${variablePrefix}__endISODate" val dateConstraints = Expression.all( Expression.any( Expression.all( Expression.has("start_decdate"), Expression.lt( Expression.get("start_decdate"), Expression.`var`(endDecimalYearVariable) ) ), Expression.all( Expression.not(Expression.has("start_decdate")), Expression.has("start_date"), Expression.lt( Expression.get("start_date"), Expression.`var`(endISODateVariable) ) ), Expression.all( Expression.not(Expression.has("start_decdate")), Expression.not(Expression.has("start_date")) ) ), Expression.any( Expression.all( Expression.has("end_decdate"), Expression.gte( Expression.get("end_decdate"), Expression.`var`(startDecimalYearVariable) ) ), Expression.all( Expression.not(Expression.has("end_decdate")), Expression.has("end_date"), Expression.gte( Expression.get("end_date"), Expression.`var`(startISODateVariable) ) ), Expression.all( Expression.not(Expression.has("end_decdate")), Expression.not(Expression.has("end_date")) ) ) ) val finalExpression = if (filter != null) { Expression.all(dateConstraints, filter) } else { dateConstraints } return Expression.let( Expression.literal(startDecimalYearVariable), Expression.literal(dateRange.startDecimalYear), Expression.let( Expression.literal(startISODateVariable), Expression.literal(dateRange.startDecimalYear), Expression.let( Expression.literal(endDecimalYearVariable), Expression.literal(dateRange.endDecimalYear), Expression.let( Expression.literal(endISODateVariable), Expression.literal(dateRange.endDecimalYear), finalExpression ) ) ) ) } fun updateDateRange(newDateRange: DateRange) { viewModelScope.launch { settingsDataStore.updateDateRange(newDateRange) } } }
原版参考为OpenHistoricalMap官方的maplibre-gl-dates JS实现。
问题分析与修复
1. 核心错误:ISO日期变量赋值错误
在constrainExpressionFilterByDateRange方法中,startISODateVariable和endISODateVariable被错误地赋值为小数年,但实际上应该传入ISO格式的日期字符串(比如"2020-01-01"),这直接导致基于start_date/end_date的过滤逻辑完全失效。
2. 图层遍历的潜在问题
style?.layers!!使用非空断言存在空指针风险,建议先判空再处理- 遍历所有图层会对非OHM图层(比如底图)应用过滤,可能导致不必要的性能损耗或异常
修复后的代码
@HiltViewModel class MapViewModel @Inject constructor( private val settingsDataStore: SettingsDataStore ) : ViewModel() { val dateRange = settingsDataStore.dateRange fun filterLayersByDate(map: MapLibreMap, date: Date) { val dateRange = DateRange.fromDate(date) val style = map.style ?: return // 空安全处理 // 仅处理OHM矢量图层(可根据实际图层ID前缀进一步过滤,比如"ohm-") val targetLayers = style.layers.filter { layer -> layer.source?.let { sourceId -> style.getSource(sourceId) is VectorSource } == true } targetLayers.forEach { layer -> val filteredExpr = constrainExpressionFilterByDateRange(layer.filter, dateRange) when (layer) { is LineLayer -> layer.setFilter(filteredExpr) is FillLayer -> layer.setFilter(filteredExpr) is CircleLayer -> layer.setFilter(filteredExpr) is SymbolLayer -> layer.setFilter(filteredExpr) is HeatmapLayer -> layer.setFilter(filteredExpr) is FillExtrusionLayer -> layer.setFilter(filteredExpr) } } } private fun constrainExpressionFilterByDateRange( filter: Expression? = null, dateRange: DateRange, variablePrefix: String = "maplibre_gl_dates" ): Expression { val startDecimalYearVariable = "${variablePrefix}__startDecimalYear" val startISODateVariable = "${variablePrefix}__startISODate" val endDecimalYearVariable = "${variablePrefix}__endDecimalYear" val endISODateVariable = "${variablePrefix}__endISODate" // 确保传入ISO格式日期字符串(需根据DateRange实际实现调整属性名) val startISODate = dateRange.startISO // 示例值:"1900-01-01" val endISODate = dateRange.endISO // 示例值:"2000-12-31" val dateConstraints = Expression.all( Expression.any( Expression.all( Expression.has("start_decdate"), Expression.lt( Expression.get("start_decdate"), Expression.`var`(endDecimalYearVariable) ) ), Expression.all( Expression.not(Expression.has("start_decdate")), Expression.has("start_date"), Expression.lt( Expression.get("start_date"), Expression.`var`(endISODateVariable) ) ), Expression.all( Expression.not(Expression.has("start_decdate")), Expression.not(Expression.has("start_date")) ) ), Expression.any( Expression.all( Expression.has("end_decdate"), Expression.gte( Expression.get("end_decdate"), Expression.`var`(startDecimalYearVariable) ) ), Expression.all( Expression.not(Expression.has("end_decdate")), Expression.has("end_date"), Expression.gte( Expression.get("end_date"), Expression.`var`(startISODateVariable) ) ), Expression.all( Expression.not(Expression.has("end_decdate")), Expression.not(Expression.has("end_date")) ) ) ) val finalExpression = filter?.let { Expression.all(dateConstraints, it) } ?: dateConstraints // 修正变量赋值:ISO变量传入对应日期字符串 return Expression.let( Expression.literal(startDecimalYearVariable), Expression.literal(dateRange.startDecimalYear), Expression.let( Expression.literal(startISODateVariable), Expression.literal(startISODate), Expression.let( Expression.literal(endDecimalYearVariable), Expression.literal(dateRange.endDecimalYear), Expression.let( Expression.literal(endISODateVariable), Expression.literal(endISODate), finalExpression ) ) ) ) } fun updateDateRange(newDateRange: DateRange) { viewModelScope.launch { settingsDataStore.updateDateRange(newDateRange) } } }
额外注意事项
- 确保
DateRange类包含正确的startISO和endISO属性,返回符合ISO 8601标准的日期字符串 - 如果部分国家名称仍无法移除,检查对应图层的数据源是否包含
end_decdate/end_date属性,确认过滤逻辑覆盖了所有日期字段情况 - 可通过MapLibre开发者工具查看图层的过滤表达式是否正确生成,排查具体字段的匹配问题
内容的提问来源于stack exchange,提问作者Marcel Alexandru
相关产品推荐
相关产品推荐

