terriajs-tiff-imagery-provider
Version:
Load GeoTIFF/COG(Cloud optimized GeoTIFF) on Cesium
2,730 lines • 124 kB
JavaScript
import { Color, WebMercatorTilingScheme, Rectangle, Math as Math$1, Cartesian3, Cartographic, Credit, Event, defined, Cartesian2, GeographicTilingScheme, DeveloperError, ImageryLayerFeatureInfo } from 'terriajs-cesium';
import { Pool, fromBlob, fromUrl } from 'geotiff';
const colorscales = {
viridis: new Uint8Array([68, 1, 84, 255, 68, 2, 86, 255, 69, 4, 87, 255, 69, 5, 89, 255, 70, 7, 90, 255, 70, 8, 92, 255, 70, 10, 93, 255, 70, 11, 94, 255, 71, 13, 96, 255, 71, 14, 97, 255, 71, 16, 99, 255, 71, 17, 100, 255, 71, 19, 101, 255, 72, 20, 103, 255, 72, 22, 104, 255, 72, 23, 105, 255, 72, 24, 106, 255, 72, 26, 108, 255, 72, 27, 109, 255, 72, 28, 110, 255, 72, 29, 111, 255, 72, 31, 112, 255, 72, 32, 113, 255, 72, 33, 115, 255, 72, 35, 116, 255, 72, 36, 117, 255, 72, 37, 118, 255, 72, 38, 119, 255, 72, 40, 120, 255, 72, 41, 121, 255, 71, 42, 122, 255, 71, 44, 122, 255, 71, 45, 123, 255, 71, 46, 124, 255, 71, 47, 125, 255, 70, 48, 126, 255, 70, 50, 126, 255, 70, 51, 127, 255, 70, 52, 128, 255, 69, 53, 129, 255, 69, 55, 129, 255, 69, 56, 130, 255, 68, 57, 131, 255, 68, 58, 131, 255, 68, 59, 132, 255, 67, 61, 132, 255, 67, 62, 133, 255, 66, 63, 133, 255, 66, 64, 134, 255, 66, 65, 134, 255, 65, 66, 135, 255, 65, 68, 135, 255, 64, 69, 136, 255, 64, 70, 136, 255, 63, 71, 136, 255, 63, 72, 137, 255, 62, 73, 137, 255, 62, 74, 137, 255, 62, 76, 138, 255, 61, 77, 138, 255, 61, 78, 138, 255, 60, 79, 138, 255, 60, 80, 139, 255, 59, 81, 139, 255, 59, 82, 139, 255, 58, 83, 139, 255, 58, 84, 140, 255, 57, 85, 140, 255, 57, 86, 140, 255, 56, 88, 140, 255, 56, 89, 140, 255, 55, 90, 140, 255, 55, 91, 141, 255, 54, 92, 141, 255, 54, 93, 141, 255, 53, 94, 141, 255, 53, 95, 141, 255, 52, 96, 141, 255, 52, 97, 141, 255, 51, 98, 141, 255, 51, 99, 141, 255, 50, 100, 142, 255, 50, 101, 142, 255, 49, 102, 142, 255, 49, 103, 142, 255, 49, 104, 142, 255, 48, 105, 142, 255, 48, 106, 142, 255, 47, 107, 142, 255, 47, 108, 142, 255, 46, 109, 142, 255, 46, 110, 142, 255, 46, 111, 142, 255, 45, 112, 142, 255, 45, 113, 142, 255, 44, 113, 142, 255, 44, 114, 142, 255, 44, 115, 142, 255, 43, 116, 142, 255, 43, 117, 142, 255, 42, 118, 142, 255, 42, 119, 142, 255, 42, 120, 142, 255, 41, 121, 142, 255, 41, 122, 142, 255, 41, 123, 142, 255, 40, 124, 142, 255, 40, 125, 142, 255, 39, 126, 142, 255, 39, 127, 142, 255, 39, 128, 142, 255, 38, 129, 142, 255, 38, 130, 142, 255, 38, 130, 142, 255, 37, 131, 142, 255, 37, 132, 142, 255, 37, 133, 142, 255, 36, 134, 142, 255, 36, 135, 142, 255, 35, 136, 142, 255, 35, 137, 142, 255, 35, 138, 141, 255, 34, 139, 141, 255, 34, 140, 141, 255, 34, 141, 141, 255, 33, 142, 141, 255, 33, 143, 141, 255, 33, 144, 141, 255, 33, 145, 140, 255, 32, 146, 140, 255, 32, 146, 140, 255, 32, 147, 140, 255, 31, 148, 140, 255, 31, 149, 139, 255, 31, 150, 139, 255, 31, 151, 139, 255, 31, 152, 139, 255, 31, 153, 138, 255, 31, 154, 138, 255, 30, 155, 138, 255, 30, 156, 137, 255, 30, 157, 137, 255, 31, 158, 137, 255, 31, 159, 136, 255, 31, 160, 136, 255, 31, 161, 136, 255, 31, 161, 135, 255, 31, 162, 135, 255, 32, 163, 134, 255, 32, 164, 134, 255, 33, 165, 133, 255, 33, 166, 133, 255, 34, 167, 133, 255, 34, 168, 132, 255, 35, 169, 131, 255, 36, 170, 131, 255, 37, 171, 130, 255, 37, 172, 130, 255, 38, 173, 129, 255, 39, 173, 129, 255, 40, 174, 128, 255, 41, 175, 127, 255, 42, 176, 127, 255, 44, 177, 126, 255, 45, 178, 125, 255, 46, 179, 124, 255, 47, 180, 124, 255, 49, 181, 123, 255, 50, 182, 122, 255, 52, 182, 121, 255, 53, 183, 121, 255, 55, 184, 120, 255, 56, 185, 119, 255, 58, 186, 118, 255, 59, 187, 117, 255, 61, 188, 116, 255, 63, 188, 115, 255, 64, 189, 114, 255, 66, 190, 113, 255, 68, 191, 112, 255, 70, 192, 111, 255, 72, 193, 110, 255, 74, 193, 109, 255, 76, 194, 108, 255, 78, 195, 107, 255, 80, 196, 106, 255, 82, 197, 105, 255, 84, 197, 104, 255, 86, 198, 103, 255, 88, 199, 101, 255, 90, 200, 100, 255, 92, 200, 99, 255, 94, 201, 98, 255, 96, 202, 96, 255, 99, 203, 95, 255, 101, 203, 94, 255, 103, 204, 92, 255, 105, 205, 91, 255, 108, 205, 90, 255, 110, 206, 88, 255, 112, 207, 87, 255, 115, 208, 86, 255, 117, 208, 84, 255, 119, 209, 83, 255, 122, 209, 81, 255, 124, 210, 80, 255, 127, 211, 78, 255, 129, 211, 77, 255, 132, 212, 75, 255, 134, 213, 73, 255, 137, 213, 72, 255, 139, 214, 70, 255, 142, 214, 69, 255, 144, 215, 67, 255, 147, 215, 65, 255, 149, 216, 64, 255, 152, 216, 62, 255, 155, 217, 60, 255, 157, 217, 59, 255, 160, 218, 57, 255, 162, 218, 55, 255, 165, 219, 54, 255, 168, 219, 52, 255, 170, 220, 50, 255, 173, 220, 48, 255, 176, 221, 47, 255, 178, 221, 45, 255, 181, 222, 43, 255, 184, 222, 41, 255, 186, 222, 40, 255, 189, 223, 38, 255, 192, 223, 37, 255, 194, 223, 35, 255, 197, 224, 33, 255, 200, 224, 32, 255, 202, 225, 31, 255, 205, 225, 29, 255, 208, 225, 28, 255, 210, 226, 27, 255, 213, 226, 26, 255, 216, 226, 25, 255, 218, 227, 25, 255, 221, 227, 24, 255, 223, 227, 24, 255, 226, 228, 24, 255, 229, 228, 25, 255, 231, 228, 25, 255, 234, 229, 26, 255, 236, 229, 27, 255, 239, 229, 28, 255, 241, 229, 29, 255, 244, 230, 30, 255, 246, 230, 32, 255, 248, 230, 33, 255, 251, 231, 35, 255, 253, 231, 37, 255]),
inferno: new Uint8Array([0, 0, 4, 255, 1, 0, 5, 255, 1, 1, 6, 255, 1, 1, 8, 255, 2, 1, 10, 255, 2, 2, 12, 255, 2, 2, 14, 255, 3, 2, 16, 255, 4, 3, 18, 255, 4, 3, 20, 255, 5, 4, 23, 255, 6, 4, 25, 255, 7, 5, 27, 255, 8, 5, 29, 255, 9, 6, 31, 255, 10, 7, 34, 255, 11, 7, 36, 255, 12, 8, 38, 255, 13, 8, 41, 255, 14, 9, 43, 255, 16, 9, 45, 255, 17, 10, 48, 255, 18, 10, 50, 255, 20, 11, 52, 255, 21, 11, 55, 255, 22, 11, 57, 255, 24, 12, 60, 255, 25, 12, 62, 255, 27, 12, 65, 255, 28, 12, 67, 255, 30, 12, 69, 255, 31, 12, 72, 255, 33, 12, 74, 255, 35, 12, 76, 255, 36, 12, 79, 255, 38, 12, 81, 255, 40, 11, 83, 255, 41, 11, 85, 255, 43, 11, 87, 255, 45, 11, 89, 255, 47, 10, 91, 255, 49, 10, 92, 255, 50, 10, 94, 255, 52, 10, 95, 255, 54, 9, 97, 255, 56, 9, 98, 255, 57, 9, 99, 255, 59, 9, 100, 255, 61, 9, 101, 255, 62, 9, 102, 255, 64, 10, 103, 255, 66, 10, 104, 255, 68, 10, 104, 255, 69, 10, 105, 255, 71, 11, 106, 255, 73, 11, 106, 255, 74, 12, 107, 255, 76, 12, 107, 255, 77, 13, 108, 255, 79, 13, 108, 255, 81, 14, 108, 255, 82, 14, 109, 255, 84, 15, 109, 255, 85, 15, 109, 255, 87, 16, 110, 255, 89, 16, 110, 255, 90, 17, 110, 255, 92, 18, 110, 255, 93, 18, 110, 255, 95, 19, 110, 255, 97, 19, 110, 255, 98, 20, 110, 255, 100, 21, 110, 255, 101, 21, 110, 255, 103, 22, 110, 255, 105, 22, 110, 255, 106, 23, 110, 255, 108, 24, 110, 255, 109, 24, 110, 255, 111, 25, 110, 255, 113, 25, 110, 255, 114, 26, 110, 255, 116, 26, 110, 255, 117, 27, 110, 255, 119, 28, 109, 255, 120, 28, 109, 255, 122, 29, 109, 255, 124, 29, 109, 255, 125, 30, 109, 255, 127, 30, 108, 255, 128, 31, 108, 255, 130, 32, 108, 255, 132, 32, 107, 255, 133, 33, 107, 255, 135, 33, 107, 255, 136, 34, 106, 255, 138, 34, 106, 255, 140, 35, 105, 255, 141, 35, 105, 255, 143, 36, 105, 255, 144, 37, 104, 255, 146, 37, 104, 255, 147, 38, 103, 255, 149, 38, 103, 255, 151, 39, 102, 255, 152, 39, 102, 255, 154, 40, 101, 255, 155, 41, 100, 255, 157, 41, 100, 255, 159, 42, 99, 255, 160, 42, 99, 255, 162, 43, 98, 255, 163, 44, 97, 255, 165, 44, 96, 255, 166, 45, 96, 255, 168, 46, 95, 255, 169, 46, 94, 255, 171, 47, 94, 255, 173, 48, 93, 255, 174, 48, 92, 255, 176, 49, 91, 255, 177, 50, 90, 255, 179, 50, 90, 255, 180, 51, 89, 255, 182, 52, 88, 255, 183, 53, 87, 255, 185, 53, 86, 255, 186, 54, 85, 255, 188, 55, 84, 255, 189, 56, 83, 255, 191, 57, 82, 255, 192, 58, 81, 255, 193, 58, 80, 255, 195, 59, 79, 255, 196, 60, 78, 255, 198, 61, 77, 255, 199, 62, 76, 255, 200, 63, 75, 255, 202, 64, 74, 255, 203, 65, 73, 255, 204, 66, 72, 255, 206, 67, 71, 255, 207, 68, 70, 255, 208, 69, 69, 255, 210, 70, 68, 255, 211, 71, 67, 255, 212, 72, 66, 255, 213, 74, 65, 255, 215, 75, 63, 255, 216, 76, 62, 255, 217, 77, 61, 255, 218, 78, 60, 255, 219, 80, 59, 255, 221, 81, 58, 255, 222, 82, 56, 255, 223, 83, 55, 255, 224, 85, 54, 255, 225, 86, 53, 255, 226, 87, 52, 255, 227, 89, 51, 255, 228, 90, 49, 255, 229, 92, 48, 255, 230, 93, 47, 255, 231, 94, 46, 255, 232, 96, 45, 255, 233, 97, 43, 255, 234, 99, 42, 255, 235, 100, 41, 255, 235, 102, 40, 255, 236, 103, 38, 255, 237, 105, 37, 255, 238, 106, 36, 255, 239, 108, 35, 255, 239, 110, 33, 255, 240, 111, 32, 255, 241, 113, 31, 255, 241, 115, 29, 255, 242, 116, 28, 255, 243, 118, 27, 255, 243, 120, 25, 255, 244, 121, 24, 255, 245, 123, 23, 255, 245, 125, 21, 255, 246, 126, 20, 255, 246, 128, 19, 255, 247, 130, 18, 255, 247, 132, 16, 255, 248, 133, 15, 255, 248, 135, 14, 255, 248, 137, 12, 255, 249, 139, 11, 255, 249, 140, 10, 255, 249, 142, 9, 255, 250, 144, 8, 255, 250, 146, 7, 255, 250, 148, 7, 255, 251, 150, 6, 255, 251, 151, 6, 255, 251, 153, 6, 255, 251, 155, 6, 255, 251, 157, 7, 255, 252, 159, 7, 255, 252, 161, 8, 255, 252, 163, 9, 255, 252, 165, 10, 255, 252, 166, 12, 255, 252, 168, 13, 255, 252, 170, 15, 255, 252, 172, 17, 255, 252, 174, 18, 255, 252, 176, 20, 255, 252, 178, 22, 255, 252, 180, 24, 255, 251, 182, 26, 255, 251, 184, 29, 255, 251, 186, 31, 255, 251, 188, 33, 255, 251, 190, 35, 255, 250, 192, 38, 255, 250, 194, 40, 255, 250, 196, 42, 255, 250, 198, 45, 255, 249, 199, 47, 255, 249, 201, 50, 255, 249, 203, 53, 255, 248, 205, 55, 255, 248, 207, 58, 255, 247, 209, 61, 255, 247, 211, 64, 255, 246, 213, 67, 255, 246, 215, 70, 255, 245, 217, 73, 255, 245, 219, 76, 255, 244, 221, 79, 255, 244, 223, 83, 255, 244, 225, 86, 255, 243, 227, 90, 255, 243, 229, 93, 255, 242, 230, 97, 255, 242, 232, 101, 255, 242, 234, 105, 255, 241, 236, 109, 255, 241, 237, 113, 255, 241, 239, 117, 255, 241, 241, 121, 255, 242, 242, 125, 255, 242, 244, 130, 255, 243, 245, 134, 255, 243, 246, 138, 255, 244, 248, 142, 255, 245, 249, 146, 255, 246, 250, 150, 255, 248, 251, 154, 255, 249, 252, 157, 255, 250, 253, 161, 255, 252, 255, 164, 255]),
turbo: new Uint8Array([48, 18, 59, 255, 50, 21, 67, 255, 51, 24, 74, 255, 52, 27, 81, 255, 53, 30, 88, 255, 54, 33, 95, 255, 55, 36, 102, 255, 56, 39, 109, 255, 57, 42, 115, 255, 58, 45, 121, 255, 59, 47, 128, 255, 60, 50, 134, 255, 61, 53, 139, 255, 62, 56, 145, 255, 63, 59, 151, 255, 63, 62, 156, 255, 64, 64, 162, 255, 65, 67, 167, 255, 65, 70, 172, 255, 66, 73, 177, 255, 66, 75, 181, 255, 67, 78, 186, 255, 68, 81, 191, 255, 68, 84, 195, 255, 68, 86, 199, 255, 69, 89, 203, 255, 69, 92, 207, 255, 69, 94, 211, 255, 70, 97, 214, 255, 70, 100, 218, 255, 70, 102, 221, 255, 70, 105, 224, 255, 70, 107, 227, 255, 71, 110, 230, 255, 71, 113, 233, 255, 71, 115, 235, 255, 71, 118, 238, 255, 71, 120, 240, 255, 71, 123, 242, 255, 70, 125, 244, 255, 70, 128, 246, 255, 70, 130, 248, 255, 70, 133, 250, 255, 70, 135, 251, 255, 69, 138, 252, 255, 69, 140, 253, 255, 68, 143, 254, 255, 67, 145, 254, 255, 66, 148, 255, 255, 65, 150, 255, 255, 64, 153, 255, 255, 62, 155, 254, 255, 61, 158, 254, 255, 59, 160, 253, 255, 58, 163, 252, 255, 56, 165, 251, 255, 55, 168, 250, 255, 53, 171, 248, 255, 51, 173, 247, 255, 49, 175, 245, 255, 47, 178, 244, 255, 46, 180, 242, 255, 44, 183, 240, 255, 42, 185, 238, 255, 40, 188, 235, 255, 39, 190, 233, 255, 37, 192, 231, 255, 35, 195, 228, 255, 34, 197, 226, 255, 32, 199, 223, 255, 31, 201, 221, 255, 30, 203, 218, 255, 28, 205, 216, 255, 27, 208, 213, 255, 26, 210, 210, 255, 26, 212, 208, 255, 25, 213, 205, 255, 24, 215, 202, 255, 24, 217, 200, 255, 24, 219, 197, 255, 24, 221, 194, 255, 24, 222, 192, 255, 24, 224, 189, 255, 25, 226, 187, 255, 25, 227, 185, 255, 26, 228, 182, 255, 28, 230, 180, 255, 29, 231, 178, 255, 31, 233, 175, 255, 32, 234, 172, 255, 34, 235, 170, 255, 37, 236, 167, 255, 39, 238, 164, 255, 42, 239, 161, 255, 44, 240, 158, 255, 47, 241, 155, 255, 50, 242, 152, 255, 53, 243, 148, 255, 56, 244, 145, 255, 60, 245, 142, 255, 63, 246, 138, 255, 67, 247, 135, 255, 70, 248, 132, 255, 74, 248, 128, 255, 78, 249, 125, 255, 82, 250, 122, 255, 85, 250, 118, 255, 89, 251, 115, 255, 93, 252, 111, 255, 97, 252, 108, 255, 101, 253, 105, 255, 105, 253, 102, 255, 109, 254, 98, 255, 113, 254, 95, 255, 117, 254, 92, 255, 121, 254, 89, 255, 125, 255, 86, 255, 128, 255, 83, 255, 132, 255, 81, 255, 136, 255, 78, 255, 139, 255, 75, 255, 143, 255, 73, 255, 146, 255, 71, 255, 150, 254, 68, 255, 153, 254, 66, 255, 156, 254, 64, 255, 159, 253, 63, 255, 161, 253, 61, 255, 164, 252, 60, 255, 167, 252, 58, 255, 169, 251, 57, 255, 172, 251, 56, 255, 175, 250, 55, 255, 177, 249, 54, 255, 180, 248, 54, 255, 183, 247, 53, 255, 185, 246, 53, 255, 188, 245, 52, 255, 190, 244, 52, 255, 193, 243, 52, 255, 195, 241, 52, 255, 198, 240, 52, 255, 200, 239, 52, 255, 203, 237, 52, 255, 205, 236, 52, 255, 208, 234, 52, 255, 210, 233, 53, 255, 212, 231, 53, 255, 215, 229, 53, 255, 217, 228, 54, 255, 219, 226, 54, 255, 221, 224, 55, 255, 223, 223, 55, 255, 225, 221, 55, 255, 227, 219, 56, 255, 229, 217, 56, 255, 231, 215, 57, 255, 233, 213, 57, 255, 235, 211, 57, 255, 236, 209, 58, 255, 238, 207, 58, 255, 239, 205, 58, 255, 241, 203, 58, 255, 242, 201, 58, 255, 244, 199, 58, 255, 245, 197, 58, 255, 246, 195, 58, 255, 247, 193, 58, 255, 248, 190, 57, 255, 249, 188, 57, 255, 250, 186, 57, 255, 251, 184, 56, 255, 251, 182, 55, 255, 252, 179, 54, 255, 252, 177, 54, 255, 253, 174, 53, 255, 253, 172, 52, 255, 254, 169, 51, 255, 254, 167, 50, 255, 254, 164, 49, 255, 254, 161, 48, 255, 254, 158, 47, 255, 254, 155, 45, 255, 254, 153, 44, 255, 254, 150, 43, 255, 254, 147, 42, 255, 254, 144, 41, 255, 253, 141, 39, 255, 253, 138, 38, 255, 252, 135, 37, 255, 252, 132, 35, 255, 251, 129, 34, 255, 251, 126, 33, 255, 250, 123, 31, 255, 249, 120, 30, 255, 249, 117, 29, 255, 248, 114, 28, 255, 247, 111, 26, 255, 246, 108, 25, 255, 245, 105, 24, 255, 244, 102, 23, 255, 243, 99, 21, 255, 242, 96, 20, 255, 241, 93, 19, 255, 240, 91, 18, 255, 239, 88, 17, 255, 237, 85, 16, 255, 236, 83, 15, 255, 235, 80, 14, 255, 234, 78, 13, 255, 232, 75, 12, 255, 231, 73, 12, 255, 229, 71, 11, 255, 228, 69, 10, 255, 226, 67, 10, 255, 225, 65, 9, 255, 223, 63, 8, 255, 221, 61, 8, 255, 220, 59, 7, 255, 218, 57, 7, 255, 216, 55, 6, 255, 214, 53, 6, 255, 212, 51, 5, 255, 210, 49, 5, 255, 208, 47, 5, 255, 206, 45, 4, 255, 204, 43, 4, 255, 202, 42, 4, 255, 200, 40, 3, 255, 197, 38, 3, 255, 195, 37, 3, 255, 193, 35, 2, 255, 190, 33, 2, 255, 188, 32, 2, 255, 185, 30, 2, 255, 183, 29, 2, 255, 180, 27, 1, 255, 178, 26, 1, 255, 175, 24, 1, 255, 172, 23, 1, 255, 169, 22, 1, 255, 167, 20, 1, 255, 164, 19, 1, 255, 161, 18, 1, 255, 158, 16, 1, 255, 155, 15, 1, 255, 152, 14, 1, 255, 149, 13, 1, 255, 146, 11, 1, 255, 142, 10, 1, 255, 139, 9, 2, 255, 136, 8, 2, 255, 133, 7, 2, 255, 129, 6, 2, 255, 126, 5, 2, 255, 122, 4, 3, 255]),
rainbow: {
colors: ["#96005A", "#0000C8", "#0019FF", "#0098FF", "#2CFF96", "#97FF00", "#FFEA00", "#FF6F00", "#FF0000"],
positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1]
},
jet: {
colors: ["#000083", "#003CAA", "#05FFFF", "#FFFF00", "#FA0000", "#800000"],
positions: [0, 0.125, 0.375, 0.625, 0.875, 1]
},
hsv: {
colors: ["#ff0000", "#fdff02", "#f7ff02", "#00fc04", "#00fc0a", "#01f9ff", "#0200fd", "#0800fd", "#ff00fb", "#ff00f5", "#ff0006"],
positions: [0, 0.169, 0.173, 0.337, 0.341, 0.506, 0.671, 0.675, 0.839, 0.843, 1]
},
hot: {
colors: ["#000000", "#e60000", "#ffd200", "#ffffff"],
positions: [0, 0.3, 0.6, 1]
},
cool: {
colors: ["#00ffff", "#ff00ff"],
positions: [0, 1]
},
spring: {
colors: ["#ff00ff", "#ffff00"],
positions: [0, 1]
},
summer: {
colors: ["#008066", "#ffff66"],
positions: [0, 1]
},
autumn: {
colors: ["#ff0000", "#ffff00"],
positions: [0, 1]
},
winter: {
colors: ["#0000ff", "#00ff80"],
positions: [0, 1]
},
bone: {
colors: ["#000000", "#545474", "#a9c8c8", "#ffffff"],
positions: [0, 0.376, 0.753, 1]
},
copper: {
colors: ["#000000", "#ffa066", "#ffc77f"],
positions: [0, 0.804, 1]
},
greys: {
colors: ["#000000", "#ffffff"],
positions: [0, 1]
},
ylgnbu: {
colors: ["#081d58", "#253494", "#225ea8", "#1d91c0", "#41b6c4", "#7fcdbb", "#c7e9b4", "#edf8d9", "#ffffd9"],
positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1]
},
greens: {
colors: ["#00441b", "#006d2c", "#238b45", "#41ab5d", "#74c476", "#a1d99b", "#c7e9c0", "#e5f5e0", "#f7fcf5"],
positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1]
},
ylorrd: {
colors: ["#800026", "#bd0026", "#e31a1c", "#fc4e2a", "#fd8d3c", "#feb24c", "#fed976", "#ffeda0", "#ffffcc"],
positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1]
},
bluered: {
colors: ["#0000ff", "#ff0000"],
positions: [0, 1]
},
rdbu: {
colors: ["#050aac", "#6a89f7", "#bebebe", "#dcaa84", "#e6915a", "#b20a1c"],
positions: [0, 0.35, 0.5, 0.6, 0.7, 1]
},
picnic: {
colors: ["#0000ff", "#3399ff", "#66ccff", "#99ccff", "#ccccff", "#ffffff", "#ffccff", "#ff99ff", "#ff66cc", "#ff6666", "#ff0000"],
positions: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
},
portland: {
colors: ["#0c3383", "#0a88ba", "#f2d338", "#f28f38", "#d91e1e"],
positions: [0, 0.25, 0.5, 0.75, 1]
},
blackbody: {
colors: ["#000000", "#e60000", "#e6d200", "#ffffff", "#a0c8ff"],
positions: [0, 0.2, 0.4, 0.7, 1]
},
earth: {
colors: ["#000082", "#00b4b4", "#28d228", "#e6e632", "#784614", "#ffffff"],
positions: [0, 0.1, 0.2, 0.4, 0.6, 1]
},
electric: {
colors: ["#000000", "#1e0064", "#780064", "#a05a00", "#e6c800", "#fffadc"],
positions: [0, 0.15, 0.4, 0.6, 0.8, 1]
},
magma: new Uint8Array([0, 0, 4, 255, 1, 0, 5, 255, 1, 1, 6, 255, 1, 1, 8, 255, 2, 1, 9, 255, 2, 2, 11, 255, 2, 2, 13, 255, 3, 3, 15, 255, 3, 3, 18, 255, 4, 4, 20, 255, 5, 4, 22, 255, 6, 5, 24, 255, 6, 5, 26, 255, 7, 6, 28, 255, 8, 7, 30, 255, 9, 7, 32, 255, 10, 8, 34, 255, 11, 9, 36, 255, 12, 9, 38, 255, 13, 10, 41, 255, 14, 11, 43, 255, 16, 11, 45, 255, 17, 12, 47, 255, 18, 13, 49, 255, 19, 13, 52, 255, 20, 14, 54, 255, 21, 14, 56, 255, 22, 15, 59, 255, 24, 15, 61, 255, 25, 16, 63, 255, 26, 16, 66, 255, 28, 16, 68, 255, 29, 17, 71, 255, 30, 17, 73, 255, 32, 17, 75, 255, 33, 17, 78, 255, 34, 17, 80, 255, 36, 18, 83, 255, 37, 18, 85, 255, 39, 18, 88, 255, 41, 17, 90, 255, 42, 17, 92, 255, 44, 17, 95, 255, 45, 17, 97, 255, 47, 17, 99, 255, 49, 17, 101, 255, 51, 16, 103, 255, 52, 16, 105, 255, 54, 16, 107, 255, 56, 16, 108, 255, 57, 15, 110, 255, 59, 15, 112, 255, 61, 15, 113, 255, 63, 15, 114, 255, 64, 15, 116, 255, 66, 15, 117, 255, 68, 15, 118, 255, 69, 16, 119, 255, 71, 16, 120, 255, 73, 16, 120, 255, 74, 16, 121, 255, 76, 17, 122, 255, 78, 17, 123, 255, 79, 18, 123, 255, 81, 18, 124, 255, 82, 19, 124, 255, 84, 19, 125, 255, 86, 20, 125, 255, 87, 21, 126, 255, 89, 21, 126, 255, 90, 22, 126, 255, 92, 22, 127, 255, 93, 23, 127, 255, 95, 24, 127, 255, 96, 24, 128, 255, 98, 25, 128, 255, 100, 26, 128, 255, 101, 26, 128, 255, 103, 27, 128, 255, 104, 28, 129, 255, 106, 28, 129, 255, 107, 29, 129, 255, 109, 29, 129, 255, 110, 30, 129, 255, 112, 31, 129, 255, 114, 31, 129, 255, 115, 32, 129, 255, 117, 33, 129, 255, 118, 33, 129, 255, 120, 34, 129, 255, 121, 34, 130, 255, 123, 35, 130, 255, 124, 35, 130, 255, 126, 36, 130, 255, 128, 37, 130, 255, 129, 37, 129, 255, 131, 38, 129, 255, 132, 38, 129, 255, 134, 39, 129, 255, 136, 39, 129, 255, 137, 40, 129, 255, 139, 41, 129, 255, 140, 41, 129, 255, 142, 42, 129, 255, 144, 42, 129, 255, 145, 43, 129, 255, 147, 43, 128, 255, 148, 44, 128, 255, 150, 44, 128, 255, 152, 45, 128, 255, 153, 45, 128, 255, 155, 46, 127, 255, 156, 46, 127, 255, 158, 47, 127, 255, 160, 47, 127, 255, 161, 48, 126, 255, 163, 48, 126, 255, 165, 49, 126, 255, 166, 49, 125, 255, 168, 50, 125, 255, 170, 51, 125, 255, 171, 51, 124, 255, 173, 52, 124, 255, 174, 52, 123, 255, 176, 53, 123, 255, 178, 53, 123, 255, 179, 54, 122, 255, 181, 54, 122, 255, 183, 55, 121, 255, 184, 55, 121, 255, 186, 56, 120, 255, 188, 57, 120, 255, 189, 57, 119, 255, 191, 58, 119, 255, 192, 58, 118, 255, 194, 59, 117, 255, 196, 60, 117, 255, 197, 60, 116, 255, 199, 61, 115, 255, 200, 62, 115, 255, 202, 62, 114, 255, 204, 63, 113, 255, 205, 64, 113, 255, 207, 64, 112, 255, 208, 65, 111, 255, 210, 66, 111, 255, 211, 67, 110, 255, 213, 68, 109, 255, 214, 69, 108, 255, 216, 69, 108, 255, 217, 70, 107, 255, 219, 71, 106, 255, 220, 72, 105, 255, 222, 73, 104, 255, 223, 74, 104, 255, 224, 76, 103, 255, 226, 77, 102, 255, 227, 78, 101, 255, 228, 79, 100, 255, 229, 80, 100, 255, 231, 82, 99, 255, 232, 83, 98, 255, 233, 84, 98, 255, 234, 86, 97, 255, 235, 87, 96, 255, 236, 88, 96, 255, 237, 90, 95, 255, 238, 91, 94, 255, 239, 93, 94, 255, 240, 95, 94, 255, 241, 96, 93, 255, 242, 98, 93, 255, 242, 100, 92, 255, 243, 101, 92, 255, 244, 103, 92, 255, 244, 105, 92, 255, 245, 107, 92, 255, 246, 108, 92, 255, 246, 110, 92, 255, 247, 112, 92, 255, 247, 114, 92, 255, 248, 116, 92, 255, 248, 118, 92, 255, 249, 120, 93, 255, 249, 121, 93, 255, 249, 123, 93, 255, 250, 125, 94, 255, 250, 127, 94, 255, 250, 129, 95, 255, 251, 131, 95, 255, 251, 133, 96, 255, 251, 135, 97, 255, 252, 137, 97, 255, 252, 138, 98, 255, 252, 140, 99, 255, 252, 142, 100, 255, 252, 144, 101, 255, 253, 146, 102, 255, 253, 148, 103, 255, 253, 150, 104, 255, 253, 152, 105, 255, 253, 154, 106, 255, 253, 155, 107, 255, 254, 157, 108, 255, 254, 159, 109, 255, 254, 161, 110, 255, 254, 163, 111, 255, 254, 165, 113, 255, 254, 167, 114, 255, 254, 169, 115, 255, 254, 170, 116, 255, 254, 172, 118, 255, 254, 174, 119, 255, 254, 176, 120, 255, 254, 178, 122, 255, 254, 180, 123, 255, 254, 182, 124, 255, 254, 183, 126, 255, 254, 185, 127, 255, 254, 187, 129, 255, 254, 189, 130, 255, 254, 191, 132, 255, 254, 193, 133, 255, 254, 194, 135, 255, 254, 196, 136, 255, 254, 198, 138, 255, 254, 200, 140, 255, 254, 202, 141, 255, 254, 204, 143, 255, 254, 205, 144, 255, 254, 207, 146, 255, 254, 209, 148, 255, 254, 211, 149, 255, 254, 213, 151, 255, 254, 215, 153, 255, 254, 216, 154, 255, 253, 218, 156, 255, 253, 220, 158, 255, 253, 222, 160, 255, 253, 224, 161, 255, 253, 226, 163, 255, 253, 227, 165, 255, 253, 229, 167, 255, 253, 231, 169, 255, 253, 233, 170, 255, 253, 235, 172, 255, 252, 236, 174, 255, 252, 238, 176, 255, 252, 240, 178, 255, 252, 242, 180, 255, 252, 244, 182, 255, 252, 246, 184, 255, 252, 247, 185, 255, 252, 249, 187, 255, 252, 251, 189, 255, 252, 253, 191, 255]),
plasma: new Uint8Array([13, 8, 135, 255, 16, 7, 136, 255, 19, 7, 137, 255, 22, 7, 138, 255, 25, 6, 140, 255, 27, 6, 141, 255, 29, 6, 142, 255, 32, 6, 143, 255, 34, 6, 144, 255, 36, 6, 145, 255, 38, 5, 145, 255, 40, 5, 146, 255, 42, 5, 147, 255, 44, 5, 148, 255, 46, 5, 149, 255, 47, 5, 150, 255, 49, 5, 151, 255, 51, 5, 151, 255, 53, 4, 152, 255, 55, 4, 153, 255, 56, 4, 154, 255, 58, 4, 154, 255, 60, 4, 155, 255, 62, 4, 156, 255, 63, 4, 156, 255, 65, 4, 157, 255, 67, 3, 158, 255, 68, 3, 158, 255, 70, 3, 159, 255, 72, 3, 159, 255, 73, 3, 160, 255, 75, 3, 161, 255, 76, 2, 161, 255, 78, 2, 162, 255, 80, 2, 162, 255, 81, 2, 163, 255, 83, 2, 163, 255, 85, 2, 164, 255, 86, 1, 164, 255, 88, 1, 164, 255, 89, 1, 165, 255, 91, 1, 165, 255, 92, 1, 166, 255, 94, 1, 166, 255, 96, 1, 166, 255, 97, 0, 167, 255, 99, 0, 167, 255, 100, 0, 167, 255, 102, 0, 167, 255, 103, 0, 168, 255, 105, 0, 168, 255, 106, 0, 168, 255, 108, 0, 168, 255, 110, 0, 168, 255, 111, 0, 168, 255, 113, 0, 168, 255, 114, 1, 168, 255, 116, 1, 168, 255, 117, 1, 168, 255, 119, 1, 168, 255, 120, 1, 168, 255, 122, 2, 168, 255, 123, 2, 168, 255, 125, 3, 168, 255, 126, 3, 168, 255, 128, 4, 168, 255, 129, 4, 167, 255, 131, 5, 167, 255, 132, 5, 167, 255, 134, 6, 166, 255, 135, 7, 166, 255, 136, 8, 166, 255, 138, 9, 165, 255, 139, 10, 165, 255, 141, 11, 165, 255, 142, 12, 164, 255, 143, 13, 164, 255, 145, 14, 163, 255, 146, 15, 163, 255, 148, 16, 162, 255, 149, 17, 161, 255, 150, 19, 161, 255, 152, 20, 160, 255, 153, 21, 159, 255, 154, 22, 159, 255, 156, 23, 158, 255, 157, 24, 157, 255, 158, 25, 157, 255, 160, 26, 156, 255, 161, 27, 155, 255, 162, 29, 154, 255, 163, 30, 154, 255, 165, 31, 153, 255, 166, 32, 152, 255, 167, 33, 151, 255, 168, 34, 150, 255, 170, 35, 149, 255, 171, 36, 148, 255, 172, 38, 148, 255, 173, 39, 147, 255, 174, 40, 146, 255, 176, 41, 145, 255, 177, 42, 144, 255, 178, 43, 143, 255, 179, 44, 142, 255, 180, 46, 141, 255, 181, 47, 140, 255, 182, 48, 139, 255, 183, 49, 138, 255, 184, 50, 137, 255, 186, 51, 136, 255, 187, 52, 136, 255, 188, 53, 135, 255, 189, 55, 134, 255, 190, 56, 133, 255, 191, 57, 132, 255, 192, 58, 131, 255, 193, 59, 130, 255, 194, 60, 129, 255, 195, 61, 128, 255, 196, 62, 127, 255, 197, 64, 126, 255, 198, 65, 125, 255, 199, 66, 124, 255, 200, 67, 123, 255, 201, 68, 122, 255, 202, 69, 122, 255, 203, 70, 121, 255, 204, 71, 120, 255, 204, 73, 119, 255, 205, 74, 118, 255, 206, 75, 117, 255, 207, 76, 116, 255, 208, 77, 115, 255, 209, 78, 114, 255, 210, 79, 113, 255, 211, 81, 113, 255, 212, 82, 112, 255, 213, 83, 111, 255, 213, 84, 110, 255, 214, 85, 109, 255, 215, 86, 108, 255, 216, 87, 107, 255, 217, 88, 106, 255, 218, 90, 106, 255, 218, 91, 105, 255, 219, 92, 104, 255, 220, 93, 103, 255, 221, 94, 102, 255, 222, 95, 101, 255, 222, 97, 100, 255, 223, 98, 99, 255, 224, 99, 99, 255, 225, 100, 98, 255, 226, 101, 97, 255, 226, 102, 96, 255, 227, 104, 95, 255, 228, 105, 94, 255, 229, 106, 93, 255, 229, 107, 93, 255, 230, 108, 92, 255, 231, 110, 91, 255, 231, 111, 90, 255, 232, 112, 89, 255, 233, 113, 88, 255, 233, 114, 87, 255, 234, 116, 87, 255, 235, 117, 86, 255, 235, 118, 85, 255, 236, 119, 84, 255, 237, 121, 83, 255, 237, 122, 82, 255, 238, 123, 81, 255, 239, 124, 81, 255, 239, 126, 80, 255, 240, 127, 79, 255, 240, 128, 78, 255, 241, 129, 77, 255, 241, 131, 76, 255, 242, 132, 75, 255, 243, 133, 75, 255, 243, 135, 74, 255, 244, 136, 73, 255, 244, 137, 72, 255, 245, 139, 71, 255, 245, 140, 70, 255, 246, 141, 69, 255, 246, 143, 68, 255, 247, 144, 68, 255, 247, 145, 67, 255, 247, 147, 66, 255, 248, 148, 65, 255, 248, 149, 64, 255, 249, 151, 63, 255, 249, 152, 62, 255, 249, 154, 62, 255, 250, 155, 61, 255, 250, 156, 60, 255, 250, 158, 59, 255, 251, 159, 58, 255, 251, 161, 57, 255, 251, 162, 56, 255, 252, 163, 56, 255, 252, 165, 55, 255, 252, 166, 54, 255, 252, 168, 53, 255, 252, 169, 52, 255, 253, 171, 51, 255, 253, 172, 51, 255, 253, 174, 50, 255, 253, 175, 49, 255, 253, 177, 48, 255, 253, 178, 47, 255, 253, 180, 47, 255, 253, 181, 46, 255, 254, 183, 45, 255, 254, 184, 44, 255, 254, 186, 44, 255, 254, 187, 43, 255, 254, 189, 42, 255, 254, 190, 42, 255, 254, 192, 41, 255, 253, 194, 41, 255, 253, 195, 40, 255, 253, 197, 39, 255, 253, 198, 39, 255, 253, 200, 39, 255, 253, 202, 38, 255, 253, 203, 38, 255, 252, 205, 37, 255, 252, 206, 37, 255, 252, 208, 37, 255, 252, 210, 37, 255, 251, 211, 36, 255, 251, 213, 36, 255, 251, 215, 36, 255, 250, 216, 36, 255, 250, 218, 36, 255, 249, 220, 36, 255, 249, 221, 37, 255, 248, 223, 37, 255, 248, 225, 37, 255, 247, 226, 37, 255, 247, 228, 37, 255, 246, 230, 38, 255, 246, 232, 38, 255, 245, 233, 38, 255, 245, 235, 39, 255, 244, 237, 39, 255, 243, 238, 39, 255, 243, 240, 39, 255, 242, 242, 39, 255, 241, 244, 38, 255, 241, 245, 37, 255, 240, 247, 36, 255, 240, 249, 33, 255]),
redblue: {
colors: ["#ff0000", "#0000ff"],
positions: [0, 1]
},
coolwarm: {
colors: ["#0000ff", "#ffffff", "#ff0000"],
positions: [0, 0.5, 1]
},
diverging_1: {
colors: ["#400040", "#3b004d", "#36005b", "#320068", "#2d0076", "#290084", "#240091", "#20009f", "#1b00ad", "#1600ba", "#1200c8", "#0d00d6", "#0900e3", "#0400f1", "#0000ff", "#0217ff", "#042eff", "#0645ff", "#095cff", "#0b73ff", "#0d8bff", "#10a2ff", "#12b9ff", "#14d0ff", "#17e7ff", "#19ffff", "#3fffff", "#66ffff", "#8cffff", "#b2ffff", "#d8ffff", "#ffffff", "#ffffd4", "#ffffaa", "#ffff7f", "#ffff54", "#ffff2a", "#ffff00", "#ffed00", "#ffdd00", "#ffcc00", "#ffba00", "#ffaa00", "#ff9900", "#ff8700", "#ff7700", "#ff6600", "#ff5400", "#ff4400", "#ff3300", "#ff2100", "#ff1100", "#ff0000", "#ff0017", "#ff002e", "#ff0045", "#ff005c", "#ff0073", "#ff008b", "#ff00a2", "#ff00b9", "#ff00d0", "#ff00e7", "#ff00ff"],
positions: [0, 0.01587301587, 0.03174603174, 0.04761904761, 0.06349206348, 0.07936507935, 0.09523809522, 0.11111111109, 0.12698412696, 0.14285714283, 0.1587301587, 0.17460317457, 0.19047619044, 0.20634920631, 0.22222222218, 0.23809523805, 0.25396825392, 0.26984126979, 0.28571428566, 0.30158730153, 0.3174603174, 0.33333333327, 0.34920634914, 0.36507936501, 0.38095238088, 0.39682539675, 0.41269841262, 0.42857142849, 0.44444444436, 0.46031746023, 0.4761904761, 0.49206349197, 0.50793650784, 0.52380952371, 0.53968253958, 0.55555555545, 0.57142857132, 0.58730158719, 0.60317460306, 0.61904761893, 0.6349206348, 0.65079365067, 0.66666666654, 0.68253968241, 0.69841269828, 0.71428571415, 0.73015873002, 0.74603174589, 0.76190476176, 0.77777777763, 0.7936507935, 0.80952380937, 0.82539682524, 0.84126984111, 0.85714285698, 0.87301587285, 0.88888888872, 0.90476190459, 0.92063492046, 0.93650793633, 0.9523809522, 0.96825396807, 0.98412698394, 1]
},
diverging_2: {
colors: ["#000000", "#030aff", "#204aff", "#3c8aff", "#77c4ff", "#f0ffff", "#f0ffff", "#f2ff7f", "#ffff00", "#ff831e", "#ff083d", "#ff00ff"],
positions: [0, 1e-10, 0.1, 0.2, 0.3333, 0.4666, 0.5333, 0.6666, 0.8, 0.9, 0.999999999999, 1]
},
blackwhite: {
colors: ["#000000", "#ffffff"],
positions: [0, 1]
},
twilight: {
colors: ["#E2D9E2", "#E0D9E2", "#DDD9E0", "#DAD8DF", "#D6D7DD", "#D2D5DB", "#CDD3D8", "#C8D0D6", "#C2CED4", "#BCCBD1", "#B6C8CF", "#B0C5CD", "#AAC2CC", "#A4BECA", "#9EBBC9", "#99B8C8", "#93B4C6", "#8EB1C5", "#89ADC5", "#85A9C4", "#80A5C3", "#7CA2C2", "#789EC2", "#759AC1", "#7196C1", "#6E92C0", "#6C8EBF", "#698ABF", "#6786BE", "#6682BD", "#647DBC", "#6379BB", "#6275BA", "#6170B9", "#606CB8", "#6067B6", "#5F62B4", "#5F5EB3", "#5F59B1", "#5E54AE", "#5E4FAC", "#5E4BA9", "#5E46A6", "#5D41A3", "#5D3CA0", "#5C379C", "#5B3298", "#5A2E93", "#59298E", "#572588", "#562182", "#531E7C", "#511A75", "#4E186F", "#4B1668", "#471461", "#44135A", "#411254", "#3D114E", "#3A1149", "#371144", "#351140", "#33113C", "#311339", "#301437", "#331237", "#351138", "#381139", "#3B113B", "#3F123D", "#43123E", "#481341", "#4D1443", "#521545", "#571647", "#5C1749", "#61184B", "#67194C", "#6C1B4E", "#711D4F", "#761F4F", "#7B2150", "#802350", "#852650", "#8A2950", "#8E2C50", "#922F50", "#963350", "#9A3750", "#9E3B50", "#A13F50", "#A54350", "#A84750", "#AB4B50", "#AE5051", "#B15452", "#B35953", "#B65D54", "#B86255", "#BA6657", "#BC6B59", "#BE705B", "#C0755E", "#C27A61", "#C37F64", "#C58468", "#C6896C", "#C78E71", "#C89275", "#C9977B", "#CA9C80", "#CCA186", "#CDA68C", "#CEAB92", "#CFAF99", "#D1B4A0", "#D2B8A7", "#D4BDAD", "#D6C1B4", "#D8C5BB", "#D9C9C2", "#DBCCC8", "#DDD0CE", "#DED3D3", "#DFD5D8", "#E0D7DB", "#E1D8DF", "#E2D9E1"],
positions: [0, 0.0078740157, 0.0157480315, 0.0236220472, 0.031496063, 0.0393700787, 0.0472440945, 0.0551181102, 0.062992126, 0.0708661417, 0.0787401575, 0.0866141732, 0.094488189, 0.1023622047, 0.1102362205, 0.1181102362, 0.125984252, 0.1338582677, 0.1417322835, 0.1496062992, 0.157480315, 0.1653543307, 0.1732283465, 0.1811023622, 0.188976378, 0.1968503937, 0.2047244094, 0.2125984252, 0.2204724409, 0.2283464567, 0.2362204724, 0.2440944882, 0.2519685039, 0.2598425197, 0.2677165354, 0.2755905512, 0.2834645669, 0.2913385827, 0.2992125984, 0.3070866142, 0.3149606299, 0.3228346457, 0.3307086614, 0.3385826772, 0.3464566929, 0.3543307087, 0.3622047244, 0.3700787402, 0.3779527559, 0.3858267717, 0.3937007874, 0.4015748031, 0.4094488189, 0.4173228346, 0.4251968504, 0.4330708661, 0.4409448819, 0.4488188976, 0.4566929134, 0.4645669291, 0.4724409449, 0.4803149606, 0.4881889764, 0.4960629921, 0.5039370079, 0.5118110236, 0.5196850394, 0.5275590551, 0.5354330709, 0.5433070866, 0.5511811024, 0.5590551181, 0.5669291339, 0.5748031496, 0.5826771654, 0.5905511811, 0.5984251969, 0.6062992126, 0.6141732283, 0.6220472441, 0.6299212598, 0.6377952756, 0.6456692913, 0.6535433071, 0.6614173228, 0.6692913386, 0.6771653543, 0.6850393701, 0.6929133858, 0.7007874016, 0.7086614173, 0.7165354331, 0.7244094488, 0.7322834646, 0.7401574803, 0.7480314961, 0.7559055118, 0.7637795276, 0.7716535433, 0.7795275591, 0.7874015748, 0.7952755906, 0.8031496063, 0.811023622, 0.8188976378, 0.8267716535, 0.8346456693, 0.842519685, 0.8503937008, 0.8582677165, 0.8661417323, 0.874015748, 0.8818897638, 0.8897637795, 0.8976377953, 0.905511811, 0.9133858268, 0.9212598425, 0.9291338583, 0.937007874, 0.9448818898, 0.9527559055, 0.9606299213, 0.968503937, 0.9763779528, 0.9842519685, 0.9921259843, 1]
},
twilight_shifted: {
colors: ["#301437", "#32123A", "#34113E", "#361142", "#391146", "#3C114B", "#3F1251", "#421257", "#46145E", "#491564", "#4C176B", "#4F1972", "#521C79", "#551F7F", "#572385", "#58278B", "#5A2B90", "#5B3095", "#5C359A", "#5D3A9E", "#5D3EA1", "#5E43A5", "#5E48A8", "#5E4DAB", "#5E52AD", "#5F57B0", "#5F5BB2", "#5F60B4", "#5F65B5", "#6069B7", "#606EB8", "#6172BA", "#6277BB", "#637BBC", "#657FBD", "#6684BD", "#6888BE", "#6B8CBF", "#6D90C0", "#7094C0", "#7398C1", "#769CC1", "#7AA0C2", "#7EA4C3", "#82A7C3", "#87ABC4", "#8CAFC5", "#91B2C6", "#96B6C7", "#9CB9C8", "#A1BDC9", "#A7C0CB", "#ADC3CD", "#B3C6CE", "#B9C9D0", "#BFCCD3", "#C5CFD5", "#CBD2D7", "#D0D4D9", "#D4D6DC", "#D8D8DE", "#DCD9DF", "#DED9E1", "#E1D9E2", "#E2D9E1", "#E1D8DF", "#E0D7DB", "#DFD5D8", "#DED3D3", "#DDD0CE", "#DBCCC8", "#D9C9C2", "#D8C5BB", "#D6C1B4", "#D4BDAD", "#D2B8A7", "#D1B4A0", "#CFAF99", "#CEAB92", "#CDA68C", "#CCA186", "#CA9C80", "#C9977B", "#C89275", "#C78E71", "#C6896C", "#C58468", "#C37F64", "#C27A61", "#C0755E", "#BE705B", "#BC6B59", "#BA6657", "#B86255", "#B65D54", "#B35953", "#B15452", "#AE5051", "#AB4B50", "#A84750", "#A54350", "#A13F50", "#9E3B50", "#9A3750", "#963350", "#922F50", "#8E2C50", "#8A2950", "#852650", "#802350", "#7B2150", "#761F4F", "#711D4F", "#6C1B4E", "#67194C", "#61184B", "#5C1749", "#571647", "#521545", "#4D1443", "#481341", "#43123E", "#3F123D", "#3B113B", "#381139", "#351138", "#331237", "#301437"],
positions: [0, 0.0078740157, 0.0157480315, 0.0236220472, 0.031496063, 0.0393700787, 0.0472440945, 0.0551181102, 0.062992126, 0.0708661417, 0.0787401575, 0.0866141732, 0.094488189, 0.1023622047, 0.1102362205, 0.1181102362, 0.125984252, 0.1338582677, 0.1417322835, 0.1496062992, 0.157480315, 0.1653543307, 0.1732283465, 0.1811023622, 0.188976378, 0.1968503937, 0.2047244094, 0.2125984252, 0.2204724409, 0.2283464567, 0.2362204724, 0.2440944882, 0.2519685039, 0.2598425197, 0.2677165354, 0.2755905512, 0.2834645669, 0.2913385827, 0.2992125984, 0.3070866142, 0.3149606299, 0.3228346457, 0.3307086614, 0.3385826772, 0.3464566929, 0.3543307087, 0.3622047244, 0.3700787402, 0.3779527559, 0.3858267717, 0.3937007874, 0.4015748031, 0.4094488189, 0.4173228346, 0.4251968504, 0.4330708661, 0.4409448819, 0.4488188976, 0.4566929134, 0.4645669291, 0.4724409449, 0.4803149606, 0.4881889764, 0.4960629921, 0.5039370079, 0.5118110236, 0.5196850394, 0.5275590551, 0.5354330709, 0.5433070866, 0.5511811024, 0.5590551181, 0.5669291339, 0.5748031496, 0.5826771654, 0.5905511811, 0.5984251969, 0.6062992126, 0.6141732283, 0.6220472441, 0.6299212598, 0.6377952756, 0.6456692913, 0.6535433071, 0.6614173228, 0.6692913386, 0.6771653543, 0.6850393701, 0.6929133858, 0.7007874016, 0.7086614173, 0.7165354331, 0.7244094488, 0.7322834646, 0.7401574803, 0.7480314961, 0.7559055118, 0.7637795276, 0.7716535433, 0.7795275591, 0.7874015748, 0.7952755906, 0.8031496063, 0.811023622, 0.8188976378, 0.8267716535, 0.8346456693, 0.842519685, 0.8503937008, 0.8582677165, 0.8661417323, 0.874015748, 0.8818897638, 0.8897637795, 0.8976377953, 0.905511811, 0.9133858268, 0.9212598425, 0.9291338583, 0.937007874, 0.9448818898, 0.9527559055, 0.9606299213, 0.968503937, 0.9763779528, 0.9842519685, 0.9921259843, 1]
}
};
function peg$subclass(child, parent) {
function C() {
this.constructor = child;
}
C.prototype = parent.prototype;
child.prototype = new C();
}
function peg$SyntaxError(message, expected, found, location) {
this.message = message;
this.expected = expected;
this.found = found;
this.location = location;
this.name = "SyntaxError";
if (typeof Error.captureStackTrace === "function") {
Error.captureStackTrace(this, peg$SyntaxError);
}
}
peg$subclass(peg$SyntaxError, Error);
peg$SyntaxError.buildMessage = function(expected, found) {
var DESCRIBE_EXPECTATION_FNS = {
literal: function(expectation) {
return '"' + literalEscape(expectation.text) + '"';
},
class: function(expectation) {
var escapedParts = expectation.parts.map(function(part) {
return Array.isArray(part) ? classEscape(part[0]) + "-" + classEscape(part[1]) : classEscape(part);
});
return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
},
any: function() {
return "any character";
},
end: function() {
return "end of input";
},
other: function(expectation) {
return expectation.description;
},
not: function(expectation) {
return "not " + describeExpectation(expectation.expected);
}
};
function hex(ch) {
return ch.charCodeAt(0).toString(16).toUpperCase();
}
function literalEscape(s) {
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, function(ch) {
return "\\x0" + hex(ch);
}).replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) {
return "\\x" + hex(ch);
});
}
function classEscape(s) {
return s.replace(/\\/g, "\\\\").replace(/\]/g, "\\]").replace(/\^/g, "\\^").replace(/-/g, "\\-").replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, function(ch) {
return "\\x0" + hex(ch);
}).replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) {
return "\\x" + hex(ch);
});
}
function describeExpectation(expectation) {
return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
}
function describeExpected(expected2) {
var descriptions = expected2.map(describeExpectation);
var i, j;
descriptions.sort();
if (descriptions.length > 0) {
for (i = 1, j = 1; i < descriptions.length; i++) {
if (descriptions[i - 1] !== descriptions[i]) {
descriptions[j] = descriptions[i];
j++;
}
}
descriptions.length = j;
}
switch (descriptions.length) {
case 1:
return descriptions[0];
case 2:
return descriptions[0] + " or " + descriptions[1];
default:
return descriptions.slice(0, -1).join(", ") + ", or " + descriptions[descriptions.length - 1];
}
}
function describeFound(found2) {
return found2 ? '"' + literalEscape(found2) + '"' : "end of input";
}
return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
};
function peg$parse(input, options) {
options = options !== void 0 ? options : {};
var peg$FAILED = {};
var peg$startRuleFunctions = { Expression: peg$parseExpression };
var peg$startRuleFunction = peg$parseExpression;
var peg$c0 = "+";
var peg$c1 = "-";
var peg$c2 = "*";
var peg$c3 = "/";
var peg$c4 = "**";
var peg$c5 = "(";
var peg$c6 = ")";
var peg$r0 = /^[\-+]/;
var peg$r1 = /^[0-9]/;
var peg$r2 = /^[a-zA-Z_$]/;
var peg$r3 = /^[a-zA-Z_$0-9]/;
var peg$r4 = /^[ \t\n\r]/;
var peg$e0 = peg$literalExpectation("+", false);
var peg$e1 = peg$literalExpectation("-", false);
var peg$e2 = peg$literalExpectation("*", false);
var peg$e3 = peg$literalExpectation("/", false);
var peg$e4 = peg$literalExpectation("**", false);
var peg$e5 = peg$literalExpectation("(", false);
var peg$e6 = peg$literalExpectation(")", false);
var peg$e7 = peg$otherExpectation("float");
var peg$e8 = peg$otherExpectation("integer");
var peg$e9 = peg$otherExpectation("identifier");
var peg$e10 = peg$otherExpectation("whitespace");
var peg$f0 = function(head, tail) {
var lhs = head;
var i, op;
for (i = 0; i < tail.length; i++) {
op = tail[i][1];
lhs = makeNode(lhs, tail[i][3], op);
}
return lhs;
};
var peg$f1 = function(head, tail) {
var lhs = head;
var i, op;
for (i = 0; i < tail.length; i++) {
op = tail[i][1];
lhs = makeNode(lhs, tail[i][3], op);
}
return lhs;
};
var peg$f2 = function(expr) {
return expr;
};
var peg$f3 = function(n, e) {
return { fn: n, lhs: e };
};
var peg$f4 = function() {
return parseFloat(text(), 10);
};
var peg$f5 = function() {
return parseInt(text(), 10);
};
var peg$f6 = function() {
return text();
};
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
var peg$expected = [];
var peg$silentFails = 0;
var peg$result;
if ("startRule" in options) {
if (!(options.startRule in peg$startRuleFunctions)) {
throw new Error(`Can't start parsing from rule "` + options.startRule + '".');
}
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
}
function text() {
return input.substring(peg$savedPos, peg$currPos);
}
function peg$literalExpectation(text2, ignoreCase) {
return { type: "literal", text: text2, ignoreCase };
}
function peg$endExpectation() {
return { type: "end" };
}
function peg$otherExpectation(description) {
return { type: "other", description };
}
function peg$computePosDetails(pos) {
var details = peg$posDetailsCache[pos];
var p;
if (details) {
return details;
} else {
p = pos - 1;
while (!peg$posDetailsCache[p]) {
p--;
}
details = peg$posDetailsCache[p];
details = {
line: details.line,
column: details.column
};
while (p < pos) {
if (input.charCodeAt(p) === 10) {
details.line++;
details.column = 1;
} else {
details.column++;
}
p++;
}
peg$posDetailsCache[pos] = details;
return details;
}
}
var peg$VALIDFILENAME = typeof options.filename === "string" && options.filename.length > 0;
function peg$computeLocation(startPos, endPos) {
var loc = {};
if (peg$VALIDFILENAME)
loc.filename = options.filename;
var startPosDetails = peg$computePosDetails(startPos);
loc.start = {
offset: startPos,
line: startPosDetails.line,
column: startPosDetails.column
};
var endPosDetails = peg$computePosDetails(endPos);
loc.end = {
offset: endPos,
line: endPosDetails.line,
column: endPosDetails.column
};
return loc;
}
function peg$begin() {
peg$expected.push({ pos: peg$currPos, variants: [] });
}
function peg$expect(expected2) {
var top = peg$expected[peg$expected.length - 1];
if (peg$currPos < top.pos) {
return;
}
if (peg$currPos > top.pos) {
top.pos = peg$currPos;
top.variants = [];
}
top.variants.push(expected2);
}
function peg$buildStructuredError(expected2, found, location2) {
return new peg$SyntaxError(
peg$SyntaxError.buildMessage(expected2, found),
expected2,
found,
location2
);
}
function peg$buildError() {
var expected2 = peg$expected[0];
var failPos = expected2.pos;
return peg$buildStructuredError(
expected2.variants,
failPos < input.length ? input.charAt(failPos) : null,
failPos < input.length ? peg$computeLocation(failPos, failPos + 1) : peg$computeLocation(failPos, failPos)
);
}
function peg$parseExpression() {
var s0, s1, s2, s3, s4, s5, s6, s7;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
s0 = peg$currPos;
s1 = peg$parseTerm();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$currPos;
s4 = peg$parse_();
rule$expects(peg$e0);
if (input.charCodeAt(peg$currPos) === 43) {
s5 = peg$c0;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
if (s5 === peg$FAILED) {
rule$expects(peg$e1);
if (input.charCodeAt(peg$currPos) === 45) {
s5 = peg$c1;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
}
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
s7 = peg$parseTerm();
if (s7 !== peg$FAILED) {
s4 = [s4, s5, s6, s7];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$currPos;
s4 = peg$parse_();
rule$expects(peg$e0);
if (input.charCodeAt(peg$currPos) === 43) {
s5 = peg$c0;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
if (s5 === peg$FAILED) {
rule$expects(peg$e1);
if (input.charCodeAt(peg$currPos) === 45) {
s5 = peg$c1;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
}
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
s7 = peg$parseTerm();
if (s7 !== peg$FAILED) {
s4 = [s4, s5, s6, s7];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
peg$savedPos = s0;
s0 = peg$f0(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseTerm() {
var s0, s1, s2, s3, s4, s5, s6, s7;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
s0 = peg$currPos;
s1 = peg$parseExponential();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$currPos;
s4 = peg$parse_();
rule$expects(peg$e2);
if (input.charCodeAt(peg$currPos) === 42) {
s5 = peg$c2;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
if (s5 === peg$FAILED) {
rule$expects(peg$e3);
if (input.charCodeAt(peg$currPos) === 47) {
s5 = peg$c3;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
}
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
s7 = peg$parseExponential();
if (s7 !== peg$FAILED) {
s4 = [s4, s5, s6, s7];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$currPos;
s4 = peg$parse_();
rule$expects(peg$e2);
if (input.charCodeAt(peg$currPos) === 42) {
s5 = peg$c2;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
if (s5 === peg$FAILED) {
rule$expects(peg$e3);
if (input.charCodeAt(peg$currPos) === 47) {
s5 = peg$c3;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
}
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
s7 = peg$parseExponential();
if (s7 !== peg$FAILED) {
s4 = [s4, s5, s6, s7];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
peg$savedPos = s0;
s0 = peg$f1(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseExponential() {
var s0, s1, s2, s3, s4, s5, s6, s7;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
s0 = peg$currPos;
s1 = peg$parseFactor();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$currPos;
s4 = peg$parse_();
rule$expects(peg$e4);
if (input.substr(peg$currPos, 2) === peg$c4) {
s5 = peg$c4;
peg$currPos += 2;
} else {
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
s7 = peg$parseFactor();
if (s7 !== peg$FAILED) {
s4 = [s4, s5, s6, s7];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$currPos;
s4 = peg$parse_();
rule$expects(peg$e4);
if (input.substr(peg$currPos, 2) === peg$c4) {
s5 = peg$c4;
peg$currPos += 2;
} else {
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
s7 = peg$parseFactor();
if (s7 !== peg$FAILED) {
s4 = [s4, s5, s6, s7];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
peg$savedPos = s0;
s0 = peg$f1(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseFactor() {
var s0, s1, s2, s3, s4, s5;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
s0 = peg$currPos;
rule$expects(peg$e5);
if (input.charCodeAt(peg$currPos) === 40) {
s1 = peg$c5;
peg$currPos++;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$parse_();
s3 = peg$parseExpression();
if (s3 !== peg$FAILED) {
s4 = peg$parse_();
rule$expects(peg$e6);
if (input.charCodeAt(peg$currPos) === 41) {
s5 = peg$c6;
peg$currPos++;
} else {
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f2(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parseFloat();
if (s0 === peg$FAILED) {
s0 = peg$parseInteger();
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseIdentifier();
if (s1 !== peg$FAILED) {
rule$expects(peg$e5);
if (input.charCodeAt(peg$currPos) === 40) {
s2 = peg$c5;
peg$currPos++;
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseExpression();
if (s3 !== peg$FAILED) {
rule$expects(peg$e6);
if (input.charCodeAt(peg$currPos) === 41) {
s4 = peg$c6;
peg$currPos++;
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f3(s1, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parseIdentifier();
}
}
}
}
return s0;
}
function peg$parseFloat() {
var s0, s1, s2, s3, s4, s5;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
rule$expects(peg$e7);
peg$silentFails++;
s0 = peg$currPos;
s1 = [];
if (peg$r0.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
}
while (s2 !== peg$FAILED) {
s1.push(s2);
if (peg$r0.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
}
}
s2 = [];
if (peg$r1.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
if (peg$r1.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
if (input.length > peg$currPos) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s4 = [];
if (peg$r1.test(input.charAt(peg$currPos))) {
s5 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
if (peg$r1.test(input.charAt(peg$currPos))) {
s5 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s5 = peg$FAILED;
}
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f4();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$silentFails--;
return s0;
}
function peg$parseInteger() {
var s0, s1, s2, s3;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
rule$expects(peg$e8);
peg$silentFails++;
s0 = peg$currPos;
s1 = [];
if (peg$r0.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
}
while (s2 !== peg$FAILED) {
s1.push(s2);
if (peg$r0.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
}
}
s2 = [];
if (peg$r1.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
if (peg$r1.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f5();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$silentFails--;
return s0;
}
function peg$parseIdentifier() {
var s0, s1, s2, s3, s4;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
rule$expects(peg$e9);
peg$silentFails++;
s0 = peg$currPos;
s1 = [];
if (peg$r0.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
}
while (s2 !== peg$FAILED) {
s1.push(s2);
if (peg$r0.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
}
}
if (peg$r2.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = [];
if (peg$r3.test(input.charAt(peg$currPos))) {
s4 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
if (peg$r3.test(input.charAt(peg$currPos))) {
s4 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s4 = peg$FAILED;
}
}
peg$savedPos = s0;
s0 = peg$f6();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$silentFails--;
return s0;
}
function peg$parse_() {
var s0, s1;
var rule$expects = function(expected2) {
if (peg$silentFails === 0)
peg$expect(expected2);
};
rule$expects(peg$e10);
peg$silentFails++;
s0 = [];
if (peg$r4.test(input.charAt(peg$currPos))) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
}
while (s1 !== peg$FAILED) {
s0.push(s1);
if (peg$r4.test(input.charAt(peg$currPos))) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
}
}
peg$silentFails--;
return s0;
}
function makeNode(lhs, rhs, op) {
if (typeof lhs === "number" && typeof rhs === "number") {
switch (op) {
case "+":
return lhs + rhs;
case "-":
return lhs - rhs;
case "*":
return lhs * rhs;
case "/":
return lhs / rhs;
case "**":
return Math.pow(lhs, rhs);
}
}
return {
lhs,
rhs,
op
};
}
peg$begin();
peg$result = peg$startRuleFunction();
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$expect(peg$endExpectation());
}
throw peg$buildError();
}
}
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function defaultFor(arg, val) {
return typeof arg !== "undefined" ? arg : val;
}
function create3DContext(canvas, optAttribs) {
const names = ["webgl", "experimental-webgl"];
let context = null;
for (let ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii], optAttribs);
} catch (e) {
}
if (context) {
break;
}
}
if (!context || !context.getExtension("OES_texture_float")) {
return null;
}
return context;
}
function createProgram(gl, vertexShaderSource2, fragmentShaderSource2) {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource2);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(vertexShader));
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource2);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(fragmentShader));
}
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
return program;
}
function setRectangle(gl, x, y, width, height) {
const x1 = x;
const x2 = x + width;
const y1 = y;
const y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1,
y1,
x2,
y1,
x1,
y2,
x1,
y2,
x2,
y1,
x2,
y2
]), gl.STATIC_DRAW);
}
function createDataset(gl, id, data, width, height) {
let textureData;
if (gl) {
gl.viewport(0, 0, width, height);
textureData = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, textureData);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.LUMINANCE,
width,
height,
0,
gl.LUMINANCE,
gl.FLOAT,
new Float32Array(data)
);
}
return { textureData, width, height, data, id };
}
function destroyDataset(gl, dataset) {
if (gl) {
gl.deleteTexture(dataset.textureData);
}
}
function addColorScale(name, colors, positions) {
if (colors.length !== positions.length) {
throw new Error("Invalid color scale.");
}
colorscales[name] = { colors, positions };
}
function renderColorScaleToCanvas(name, canvas, type = "continuous") {
const csDef = colorscales[name];
canvas.height = 1;
const ctx = canvas.getContext("2d");
const width = 8192;
if (!ctx) {
throw new Error("Unable to get canvas context.");
}
if (Object.prototype.toString.call(csDef) === "[object Object]") {
canvas.width = width;
if (type === "continuous") {
const gradient = ctx.createLinearGradient(0, 0, width, 1);
for (let i = 0; i < csDef.colors.length; ++i) {
gradient.addColorStop(csDef.positions[i], csDef.colors[i]);
}
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, 1);
} else if (type === "discrete") {
for (let i = 0; i < csDef.colors.length; ++i) {
const nowPos = csDef.positions[i], lastPos = csDef.positions[i + 1];
const startPos = nowPos * (width - 10);
const endPos = lastPos ? lastPos * (width - 10) : width;
ctx.fillStyle = csDef.colors[i];
ctx.fillRect(startPos, 0, endPos - startPos, 1);
}
} else {
throw new Error("Invalid color scale type.");
}
} else if (Object.prototype.toString.call(csDef) === "[object Uint8Array]") {
canvas.width = width;
const imgData = ctx.createImageData(width, 1);
imgData.data.set(csDef);
ctx.putImageData(imgData, 0, 0);
} else {
throw new Error("Color scale not defined.");
}
}
const vertexShaderSource = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform mat3 u_matrix;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// apply transformation matrix
vec2 position = (u_matrix * vec3(a_position, 1)).xy;
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}`;
const fragmentShaderSource = `
precision mediump float;
// our texture
uniform sampler2D u_textureData;
uniform sampler2D u_textureScale;
uniform vec2 u_textureSize;
uniform vec2 u_domain;
uniform vec2 u_display_range;
uniform bool u_apply_display_range;
uniform float u_noDataValue;
uniform bool u_clampLow;
uniform bool u_clampHigh;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
bool isnan( float val ) {
return ( val < 0.0 || 0.0 < val || val == 0.0 ) ? false : true;
}
void main() {
vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
float value = texture2D(u_textureData, v_texCoord)[0];
if(value < -3.402823466e+38) // Check for possible NaN value
gl_FragColor = vec4(0.0, 0, 0, 0.0);
else if (value == u_noDataValue || isnan(value))
gl_FragColor = vec4(0.0, 0, 0, 0.0);
else if (u_apply_display_range && (value < u_display_range[0] || value >= u_display_range[1]))
gl_FragColor = vec4(0.0, 0, 0, 0.0);
else if ((!u_clampLow && value < u_domain[0]) || (!u_clampHigh && value > u_domain[1]))
gl_FragColor = vec4(0, 0, 0, 0);
else {
float normalisedValue = (value - u_domain[0]) / (u_domain[1] - u_domain[0]);
gl_FragColor = texture2D(u_textureScale, vec2(normalisedValue, 0));
}
}`;
class plot {
canvas;
currentDataset;
datasetCollection;
gl;
program;
texCoordBuffer;
ctx;
displayRange;
applyDisplayRange;
matrix;
colorScaleImage;
domain;
colorScaleCanvas;
name;
clampLow;
clampHigh;
textureScale;
noDataValue;
expressionAst;
colorType = "continuous";
positionBuffer;
programCache = {};
constructor(options) {
this.datasetCollection = {};
this.currentDataset = null;
this.setCanvas(options.canvas);
this.setColorType(options.type);
if (defaultFor(options.useWebGL, true)) {
const gl = create3DContext(this.canvas, { premultipliedAlpha: false });
if (gl !== null) {
this.gl = gl;
this.program = createProgram(gl, vertexShaderSource, fragmentShaderSource);
gl.useProgram(this.program);
const texCoordLocation = gl.getAttribLocation(this.program, "a_texCoord");
this.texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0,
0,
1,
0,
0,
1,
0,
1,
1,
0,
1,
1
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
this.positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0,
0,
1,
0,
0,
1,
0,
1,
1,
0,
1,
1
]), gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(this.program, "a_position");
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
} else {
this.ctx = this.canvas.getContext("2d");
}
} else {
this.ctx = this.canvas.getContext("2d");
}
if (options.colorScaleImage) {
this.setColorScaleImage(options.colorScaleImage);
} else {
this.setColorScale(defaultFor(options.colorScale, "viridis"));
}
this.setDomain(defaultFor(options.domain, [0, 1]));
this.displayRange = defaultFor(options.displayRange, [0, 1]);
this.applyDisplayRange = defaultFor(options.applyDisplayRange, false);
this.setClamp(defaultFor(options.clampLow, true), options.clampHigh);
this.setNoDataValue(options.noDataValue);
if (options.data) {
const l = options.data.length;
this.setData(
options.data,
defaultFor(options.width, options.data[l - 2]),
defaultFor(options.height, options.data[l - 2])
);
}
if (options.datasets) {
for (let i = 0; i < options.datasets.length; ++i) {
const ds = options.datasets[i];
this.addDataset(ds.id, ds.data, ds.width, ds.height);
}
}
if (options.matrix) {
this.matrix = options.matrix;
} else {
this.matrix = [
1,
0,
0,
0,
1,
0,
0,
0,
1
];
}
}
/**
* Get the raw data from the currently selected dataset.
* @returns {TypedArray} the data of the currently selected dataset.
*/
getData() {
const dataset = this.currentDataset;
if (!dataset) {
throw new Error("No dataset available.");
}
return dataset.data;
}
/**
* Query the raw raster data at the specified coordinates.
* @param {Number} x the x coordinate
* @param {Number} y the y coordinate
* @returns {Number} the value at the specified coordinates
*/
atPoint(x, y) {
const dataset = this.currentDataset;
if (!dataset) {
throw new Error("No dataset available.");
} else if (x >= dataset.width || y >= dataset.height) {
throw new Error("Coordinates are outside of image bounds.");
}
return dataset.data[y * dataset.width + x];
}
/**
* Set the raw raster data to be rendered. This creates a new unnamed dataset.
* @param {TypedArray} data the raw raster data. This can be a typed array of
* any type, but will be coerced to Float32Array when
* beeing rendered.
* @param {number} width the width of the raster image
* @param {number} height the height of the data
*/
setData(data, width, height) {
if (this.currentDataset && this.currentDataset.id === null) {
destroyDataset(this.gl, this.currentDataset);
}
this.currentDataset = createDataset(this.gl, null, data, width, height);
}
/**
* Add a new named dataset. The semantics are the same as with @see setData.
* @param {string} id the identifier for the dataset.
* @param {TypedArray} data the raw raster data. This can be a typed array of
* any type, but will be coerced to Float32Array when
* beeing rendered.
* @param {number} width the width of the raster image
* @param {number} height the height of the data
*/
addDataset(id, data, width, height) {
if (this.datasetAvailable(id)) {
throw new Error(`There is already a dataset registered with id '${id}'`);
}
this.datasetCollection[id] = createDataset(this.gl, id, data, width, height);
if (!this.currentDataset) {
this.currentDataset = this.datasetCollection[id];
}
}
/**
* Set the current dataset to be rendered.
* @param {string} id the identifier of the dataset to be rendered.
*/
setCurrentDataset(id) {
if (!this.datasetAvailable(id)) {
throw new Error(`No such dataset registered: '${id}'`);
}
if (this.currentDataset && this.currentDataset.id === null) {
destroyDataset(this.gl, this.currentDataset);
}
this.currentDataset = this.datasetCollection[id];
}
/**
* Remove the dataset.
* @param {string} id the identifier of the dataset to be removed.
*/
removeDataset(id) {
const dataset = this.datasetCollection[id];
if (!dataset) {
throw new Error(`No such dataset registered: '${id}'`);
}
destroyDataset(this.gl, dataset);
if (this.currentDataset === dataset) {
this.currentDataset = null;
}
delete this.datasetCollection[id];
}
removeAllDataset() {
Object.keys(this.datasetCollection).forEach((id) => this.removeDataset(id));
}
/**
* Check if the dataset is available.
* @param {string} id the identifier of the dataset to check.
* @returns {Boolean} whether or not a dataset with that identifier is defined
*/
datasetAvailable(id) {
return hasOwnProperty(this.datasetCollection, id);
}
/**
* Retrieve the rendered color scale image.
* @returns {(HTMLCanvasElement|HTMLImageElement)} the canvas or image element
* for the rendered color scale
*/
getColorScaleImage() {
return this.colorScaleImage;
}
/**
* Set the canvas to draw to. When no canvas is supplied, a new canvas element
* is created.
* @param {HTMLCanvasElement | OffscreenCanvas} [canvas] the canvas element to render to.
*/
setCanvas(canvas) {
this.canvas = canvas || document.createElement("canvas");
}
setColorType(type) {
this.colorType = type ?? "continuous";
}
/**
* Set the new value domain for the rendering.
* @param {number[]} domain the value domain range in the form [low, high]
*/
setDomain(domain) {
if (!domain || domain.length !== 2) {
throw new Error("Invalid domain specified.");
}
this.domain = domain;
}
/**
* Set the display range that will be rendered, values outside of the range
* will not be rendered (transparent)
* @param {number[]} displayRange range array in the form [min, max]
*/
setDisplayRange(displayRange) {
if (!displayRange || displayRange.length !== 2) {
throw new Error("Invalid view range specified.");
}
this.displayRange = displayRange;
this.applyDisplayRange = true;
}
/**
* Get the canvas that is currently rendered to.
* @returns {HTMLCanvasElement} the canvas that is currently rendered to.
*/
getCanvas() {
return this.canvas;
}
/**
* Set the currently selected color scale.
* @param {ColorScaleNames} name the name of the colorscale. Must be registered.
*/
setColorScale(name) {
if (!hasOwnProperty(colorscales, name)) {
throw new Error(`No such color scale '${name}'`);
}
if (!this.colorScaleCanvas) {
this.colorScaleCanvas = document.createElement("canvas");
this.colorScaleCanvas.width = 256;
this.colorScaleCanvas.height = 1;
}
renderColorScaleToCanvas(name, this.colorScaleCanvas, this.colorType);
this.name = name;
this.setColorScaleImage(this.colorScaleCanvas);
}
/**
* Set the clamping for the lower and the upper border of the values. When
* clamping is enabled for either side, the values below or above will be
* clamped to the minimum/maximum color.
* @param {Boolean} clampLow whether or not the minimum shall be clamped.
* @param {Boolean} clampHigh whether or not the maxmimum shall be clamped.
* defaults to clampMin.
*/
setClamp(clampLow, clampHigh) {
this.clampLow = clampLow;
this.clampHigh = typeof clampHigh !== "undefined" ? clampHigh : clampLow;
}
/**
* Set the currently selected color scale as an image or canvas.
* @param {(HTMLCanvasElement|HTMLImageElement)} colorScaleImage the new color
* scale image
*/
setColorScaleImage(colorScaleImage) {
this.colorScaleImage = colorScaleImage;
const gl = this.gl;
if (gl) {
if (this.textureScale) {
gl.deleteTexture(this.textureScale);
}
this.textureScale = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.textureScale);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, colorScaleImage);
}
}
/**
* Set the no-data-value: a special value that will be rendered transparent.
* @param {number} noDataValue the no-data-value. Use null to clear a
* previously set no-data-value.
*/
setNoDataValue(noDataValue) {
this.noDataValue = noDataValue;
}
/**
* Render the map to the specified canvas with the given settings.
*/
render() {
const canvas = this.canvas;
const dataset = this.currentDataset;
canvas.width = dataset.width;
canvas.height = dataset.height;
let ids = this.getExpressionIds();
if (this.gl) {
this.renderWebGL(dataset, ids);
} else if (this.ctx) {
this.render2D(dataset);
}
}
getExpressionIds() {
if (!this.expressionAst)
return null;
const idsSet = /* @__PURE__ */ new Set();
const getIds = (node) => {
if (typeof node === "string") {
idsSet.add(node.replace(/[+-]/g, ""));
}
if (typeof node.lhs === "string") {
idsSet.add(node.lhs.replace(/[+-]/g, ""));
} else if (typeof node.lhs === "object") {
getIds(node.lhs);
}
if (typeof node.rhs === "string") {
idsSet.add(node.rhs.replace(/[+-]/g, ""));
} else if (typeof node.rhs === "object") {
getIds(node.rhs);
}
};
getIds(this.expressionAst);
return Array.from(idsSet);
}
renderWebGL(dataset, ids) {
const gl = this.gl;
gl.viewport(0, 0, dataset.width, dataset.height);
const program = this.getOrCreateProgram(ids);
gl.useProgram(program);
this.setupTextures(program, ids, dataset);
this.setupUniforms(program);
this.setupAttributes(program);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
getOrCreateProgram(ids) {
if (!this.expressionAst) {
return this.program;
}
const exprKey = JSON.stringify(this.expressionAst);
if (this.programCache[exprKey]) {
return this.programCache[exprKey];
}
const vertexShader = this.createVertexShader();
const fragmentShader = this.createFragmentShader(ids);
const program = createProgram(this.gl, vertexShader, fragmentShader);
this.programCache[exprKey] = program;
return program;
}
createVertexShader() {
return `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform mat3 u_matrix;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
vec2 position = (u_matrix * vec3(a_position, 1)).xy;
vec2 zeroToOne = position / u_resolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
v_texCoord = a_texCoord;
}
`;
}
createFragmentShader(ids) {
if (!ids) {
return fragmentShaderSource;
}
const expressionReducer = (node) => {
if (typeof node === "object") {
if (node.op === "**") {
return `pow(${expressionReducer(node.lhs)}, ${expressionReducer(node.rhs)})`;
}
if (node.fn) {
return `(${node.fn}(${expressionReducer(node.lhs)}))`;
}
return `(${expressionReducer(node.lhs)} ${node.op} ${expressionReducer(node.rhs)})`;
} else if (typeof node === "string") {
return `${node}_value`;
}
return `float(${node})`;
};
const compiledExpression = expressionReducer(this.expressionAst);
return `
precision mediump float;
uniform sampler2D u_textureScale;
${ids.map((id) => `uniform sampler2D u_texture_${id};`).join("\n")}
uniform vec2 u_textureSize;
uniform vec2 u_domain;
uniform vec2 u_display_range;
uniform bool u_apply_display_range;
uniform float u_noDataValue;
uniform bool u_clampLow;
uniform bool u_clampHigh;
varying vec2 v_texCoord;
void main() {
${ids.map((id) => `float ${id}_value = texture2D(u_texture_${id}, v_texCoord)[0];`).join("\n")}
float value = ${compiledExpression};
if (value == u_noDataValue)
gl_FragColor = vec4(0.0, 0, 0, 0.0);
else if (u_apply_display_range && (value < u_display_range[0] || value >= u_display_range[1]))
gl_FragColor = vec4(0.0, 0, 0, 0.0);
else if ((!u_clampLow && value < u_domain[0]) || (!u_clampHigh && value > u_domain[1]))
gl_FragColor = vec4(0, 0, 0, 0);
else {
float normalisedValue = (value - u_domain[0]) / (u_domain[1] - u_domain[0]);
gl_FragColor = texture2D(u_textureScale, vec2(normalisedValue, 0));
}
}
`;
}
setupTextures(program, ids, dataset) {
const gl = this.gl;
if (ids) {
gl.uniform1i(gl.getUniformLocation(program, "u_textureScale"), 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.textureScale);
ids.forEach((id, index) => {
const location = index + 1;
const ds = this.datasetCollection[id];
if (!ds) {
throw new Error(`No such dataset registered: '${id}'`);
}
gl.uniform1i(gl.getUniformLocation(program, `u_texture_${id}`), location);
gl.activeTexture(gl[`TEXTURE${location}`]);
gl.bindTexture(gl.TEXTURE_2D, ds.textureData);
});
} else {
gl.uniform1i(gl.getUniformLocation(program, "u_textureData"), 0);
gl.uniform1i(gl.getUniformLocation(program, "u_textureScale"), 1);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, dataset.textureData);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this.textureScale);
}
}
setupUniforms(program) {
const gl = this.gl;
const canvas = this.canvas;
gl.uniform2f(gl.getUniformLocation(program, "u_resolution"), canvas.width, canvas.height);
gl.uniform2fv(gl.getUniformLocation(program, "u_domain"), this.domain);
gl.uniform2fv(gl.getUniformLocation(program, "u_display_range"), this.displayRange);
gl.uniform1i(gl.getUniformLocation(program, "u_apply_display_range"), +this.applyDisplayRange);
gl.uniform1i(gl.getUniformLocation(program, "u_clampLow"), +this.clampLow);
gl.uniform1i(gl.getUniformLocation(program, "u_clampHigh"), +this.clampHigh);
gl.uniform1f(gl.getUniformLocation(program, "u_noDataValue"), this.noDataValue);
gl.uniformMatrix3fv(gl.getUniformLocation(program, "u_matrix"), false, this.matrix);
}
setupAttributes(program) {
const gl = this.gl;
const canvas = this.canvas;
const positionLocation = gl.getAttribLocation(program, "a_position");
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
setRectangle(gl, 0, 0, canvas.width, canvas.height);
}
render2D(dataset) {
const ctx = this.ctx;
const w = this.canvas.width;
const h = this.canvas.height;
const imageData = ctx.createImageData(w, h);
const trange = this.domain[1] - this.domain[0];
const steps = this.colorScaleCanvas.width;
const csImageData = this.colorScaleCanvas.getContext("2d").getImageData(0, 0, steps, 1).data;
let alpha;
const data = dataset.data;
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const i = y * w + x;
let c = Math.floor((data[i] - this.domain[0]) / trange * (steps - 1));
alpha = 255;
if (c < 0) {
c = 0;
if (!this.clampLow) {
alpha = 0;
}
} else if (c > 255) {
c = 255;
if (!this.clampHigh) {
alpha = 0;
}
}
if (data[i] === this.noDataValue || data[i] !== data[i]) {
alpha = 0;
} else if (this.applyDisplayRange && (data[i] < this.displayRange[0] || data[i] >= this.displayRange[1])) {
alpha = 0;
}
const index = (y * w + x) * 4;
imageData.data[index + 0] = csImageData[c * 4];
imageData.data[index + 1] = csImageData[c * 4 + 1];
imageData.data[index + 2] = csImageData[c * 4 + 2];
imageData.data[index + 3] = Math.min(alpha, csImageData[c * 4 + 3]);
}
}
ctx.putImageData(imageData, 0, 0);
}
/**
* Render the specified dataset with the current settings.
* @param {string} id the identifier of the dataset to render.
*/
renderDataset(id) {
this.setCurrentDataset(id);
return this.render();
}
/**
* Get the color for the specified value.
* @param {number} val the value to query the color for.
* @returns {Array} the 4-tuple: red, green, blue, alpha in the range 0-255.
*/
getColor(val) {
const steps = this.colorScaleCanvas.width;
const csImageData = this.colorScaleCanvas.getContext("2d").getImageData(0, 0, steps, 1).data;
const trange = this.domain[1] - this.domain[0];
let c = Math.round((val - this.domain[0]) / trange * steps);
let alpha = 255;
if (c < 0) {
c = 0;
if (!this.clampLow) {
alpha = 0;
}
}
if (c > 255) {
c = 255;
if (!this.clampHigh) {
alpha = 0;
}
}
return [
csImageData[c * 4],
csImageData[c * 4 + 1],
csImageData[c * 4 + 2],
alpha
];
}
/**
* Sets a mathematical expression to be evaluated on the plot. Expression can contain mathematical operations with integer/float values, dataset identifiers or GLSL supported functions with a single parameter.
* Supported mathematical operations are: add '+', subtract '-', multiply '*', divide '/', power '**', unary plus '+a', unary minus '-a'.
* Useful GLSL functions are for example: radians, degrees, sin, asin, cos, acos, tan, atan, log2, log, sqrt, exp2, exp, abs, sign, floor, ceil, fract.
* @param {string} expression Mathematical expression. Example: '-2 * sin(3.1415 - dataset1) ** 2'
*/
setExpression(expression) {
if (!expression || !expression.length) {
this.expressionAst = null;
} else {
this.expressionAst = peg$parse(expression);
}
}
destroy() {
var _a;
(_a = this.gl) == null ? void 0 : _a.deleteProgram(this.program);
this.removeAllDataset();
}
}
function getMinMax(data, nodata) {
let min, max;
for (let j = 0; j < data.length; j += 1) {
const val = data[j];
if (val === nodata)
continue;
if (min === void 0 && max === void 0) {
min = max = val;
continue;
}
if (val < min) {
min = val;
} else if (val > max) {
max = val;
}
}
return {
min,
max
};
}
function decimal2rgb(number) {
return Math.round(number * 255);
}
function getRange(bands, opts) {
const band = bands[opts.band];
if (!band) {
throw new Error(`Invalid band${opts.band}`);
}
const min = (opts == null ? void 0 : opts.min) ?? +band.min;
const max = (opts == null ? void 0 : opts.max) ?? +band.max;
const range = max - min;
return { min, max, range };
}
function generateColorScale(colors, minMax) {
let stops;
if (typeof colors[0] === "string") {
stops = colors.map((color, index) => [index / colors.length, color]);
} else {
const [min, max] = minMax;
stops = colors.map((item) => [(item[0] - min) / (max - min), item[1]]);
}
stops.sort((a, b) => a[0] - b[0]);
let i = stops.length - 1;
while (i > 1 && stops[i][0] >= 1 && stops[i - 1][0] >= 1) {
stops.pop();
i--;
}
if (stops[0][0] > 0) {
stops = [[0, stops[0][1]], ...stops];
}
const colorScale = {
colors: stops.map((stop) => stop[1]),
positions: stops.map((stop) => {
let s = stop[0];
if (s < 0)
return 0;
if (s > 1)
return 1;
return s;
})
};
return colorScale;
}
function findAndSortBandNumbers(str) {
const regex = /b(\d+)/g;
const bandNumbers = /* @__PURE__ */ new Set();
let match;
while ((match = regex.exec(str)) !== null) {
bandNumbers.add(parseInt(match[1]) - 1);
}
return Array.from(bandNumbers).sort((a, b) => a - b);
}
function stringColorToRgba(color) {
const newColor = Color.fromCssColorString(color);
const { red, green, blue, alpha } = newColor;
return [red, green, blue, alpha].map((val) => Math.round(val * 255));
}
function reverseArray(options) {
const { array, width, height } = options;
const reversedArray = [];
for (let row = height - 1; row >= 0; row--) {
const startIndex = row * width;
const endIndex = startIndex + width;
const rowArray = array.slice(startIndex, endIndex);
reversedArray.push(...rowArray);
}
return reversedArray;
}
class TIFFImageryProviderTilingScheme extends WebMercatorTilingScheme {
nativeRectangle;
constructor(options) {
super(options);
const { project, unproject } = options;
this.nativeRectangle = new Rectangle(options.rectangleSouthwestInMeters.x, options.rectangleSouthwestInMeters.y, options.rectangleNortheastInMeters.x, options.rectangleNortheastInMeters.y);
this._projection = {
ellipsoid: this.ellipsoid,
project(cartographic, result) {
const [x, y] = project([cartographic.longitude, cartographic.latitude].map(Math$1.toDegrees));
const z = cartographic.height;
return Cartesian3.fromElements(x, y, z, result);
},
unproject(cartesian, result) {
const [longitude, latitude] = unproject([cartesian.x, cartesian.y]);
const height = cartesian.z;
return Cartographic.fromDegrees(longitude, latitude, height, result);
}
};
const swMeters = new Cartesian3();
options.rectangleSouthwestInMeters.clone(swMeters);
const neMeters = new Cartesian3();
options.rectangleNortheastInMeters.clone(neMeters);
const seMeters = new Cartesian3(neMeters.x, swMeters.y);
const nwMeters = new Cartesian3(swMeters.x, neMeters.y);
const southwest = this.projection.unproject(swMeters);
const southeast = this.projection.unproject(seMeters);
const northwest = this.projection.unproject(nwMeters);
const northeast = this.projection.unproject(neMeters);
this._rectangle = Rectangle.fromCartographicArray([southwest, southeast, northwest, northeast]);
}
tileXYToNativeRectangle2(x, y, level) {
const rect = this.tileXYToRectangle(x, y, level);
const projection = this.projection;
const ws = projection.project(new Cartographic(rect.west, rect.south));
const wn = projection.project(new Cartographic(rect.west, rect.north));
const en = projection.project(new Cartographic(rect.east, rect.north));
const es = projection.project(new Cartographic(rect.east, rect.south));
const positions = [ws, wn, en, es];
const xx = positions.map((pos) => pos.x);
const yy = positions.map((pos) => pos.y);
return new Rectangle(
Math.min(...xx),
Math.min(...yy),
Math.max(...xx),
Math.max(...yy)
);
}
tileXYToRectangle(x, y, level) {
const rect = this.tileXYToNativeRectangle(x, y, level);
const projection = this.projection;
const ws = projection.unproject(new Cartesian3(rect.west, rect.south));
const wn = projection.unproject(new Cartesian3(rect.west, rect.north));
const en = projection.unproject(new Cartesian3(rect.east, rect.north));
const es = projection.unproject(new Cartesian3(rect.east, rect.south));
const newRect = Rectangle.fromCartographicArray([ws, wn, en, es]);
if (newRect.east < newRect.west) {
newRect.east += Math$1.TWO_PI;
}
return newRect;
}
}
function resampleNearest(data, options) {
const { sourceWidth, sourceHeight, targetWidth, targetHeight, window, buffer = 0 } = options;
const [x0, y0, x1, y1] = window;
const effectiveSourceWidth = sourceWidth - 2 * buffer;
const effectiveSourceHeight = sourceHeight - 2 * buffer;
const resampledData = copyNewSize(data, targetWidth, targetHeight);
for (let y = 0; y < targetHeight; y++) {
for (let x = 0; x < targetWidth; x++) {
const col = buffer + (effectiveSourceWidth * (x0 + x / targetWidth * (x1 - x0)) >>> 0);
const row = buffer + (effectiveSourceHeight * (y0 + y / targetHeight * (y1 - y0)) >>> 0);
resampledData[y * targetWidth + x] = data[row * sourceWidth + col];
}
}
return resampledData;
}
function resampleData(data, options) {
const { method = "nearest" } = options;
switch (method) {
case "nearest":
return resampleNearest(data, options);
case "bilinear":
return resampleBilinear(data, options);
}
}
function copyNewSize(array, width, height, samplesPerPixel = 1) {
return new (Object.getPrototypeOf(array)).constructor(width * height * samplesPerPixel);
}
function lerp(v0, v1, t) {
return (1 - t) * v0 + t * v1;
}
function resampleBilinear(data, options) {
const { sourceWidth, sourceHeight, targetWidth, targetHeight, window, buffer = 0, nodata } = options;
const [x0, y0, x1, y1] = window;
const windowWidth = x1 - x0;
const windowHeight = y1 - y0;
const newArray = copyNewSize(data, targetWidth, targetHeight);
const effectiveSourceWidth = sourceWidth - 2 * buffer;
const effectiveSourceHeight = sourceHeight - 2 * buffer;
const invTargetWidth = 1 / targetWidth;
const invTargetHeight = 1 / targetHeight;
const isNodata = (value) => {
if (nodata === void 0)
return false;
if (nodata === 0)
return value === 0;
return Math.abs((value - nodata) / nodata) < 1e-6;
};
for (let y = 0; y < targetHeight; y++) {
const yRatio = y * invTargetHeight;
const yMapped = y0 + yRatio * windowHeight;
const rawY = effectiveSourceHeight * yMapped + buffer;
const yl = Math.floor(rawY);
const yh = Math.min(Math.ceil(rawY), sourceHeight - buffer - 1);
const ty = rawY - yl;
for (let x = 0; x < targetWidth; x++) {
const xRatio = x * invTargetWidth;
const xMapped = x0 + xRatio * windowWidth;
const rawX = effectiveSourceWidth * xMapped + buffer;
const xl = Math.floor(rawX);
const xh = Math.min(Math.ceil(rawX), sourceWidth - buffer - 1);
const txFraction = rawX - xl;
const center = data[yl * sourceWidth + xl];
const neighbors = [
data[yl * sourceWidth + xl],
data[yl * sourceWidth + xh],
data[yh * sourceWidth + xl],
data[yh * sourceWidth + xh]
];
neighbors.forEach((neighbor, index) => {
if (isNodata(neighbor)) {
neighbors[index] = center;
}
});
if (isNodata(center)) {
newArray[y * targetWidth + x] = nodata;
continue;
}
const [ll, hl, lh, hh] = neighbors;
const v0 = lerp(ll, hl, txFraction);
const v1 = lerp(lh, hh, txFraction);
const value = lerp(v0, v1, ty);
newArray[y * targetWidth + x] = value;
}
}
return newArray;
}
function inRange(val, range) {
if (val < range[0] && val < range[1] || val > range[0] && val > range[1]) {
return false;
} else {
return true;
}
}
function reprojection(options) {
const { data, sourceBBox, targetBBox, project, sourceWidth, sourceHeight, nodata } = options;
const { targetWidth = sourceWidth, targetHeight = sourceHeight } = options;
const [minX, minY, maxX, maxY] = sourceBBox;
const [minLon, minLat, maxLon, maxLat] = targetBBox;
const stepX = Math.abs(maxX - minX) / sourceWidth;
const stepY = Math.abs(maxY - minY) / sourceHeight;
const stepLon = Math.abs(maxLon - minLon) / targetWidth;
const stepLat = Math.abs(maxLat - minLat) / targetHeight;
const result = copyNewSize(data, targetWidth, targetHeight).fill(nodata);
for (let i = 0; i < targetHeight; i++) {
for (let j = 0; j < targetWidth; j++) {
const lon = minLon + stepLon * (j + 0.5);
const lat = maxLat - stepLat * (i + 0.5);
const [x, y] = project([lon, lat]);
if (!inRange(x, [minX, maxX]) || !inRange(y, [minX, maxY])) {
break;
}
const indexX = ~~((x - minX) / stepX);
const indexY = ~~((maxY - y) / stepY);
const sourceVal = data[indexY * sourceWidth + indexX];
const index = i * targetWidth + j;
result[index] = sourceVal;
}
}
return result;
}
async function generateImage(opts) {
const { data, width, height, renderOptions, bands, noData, colorMapping } = opts;
const imageData = new Uint8ClampedArray(width * height * 4);
function ifNoDataFunc(...vals) {
if (vals.some((val) => isNaN(val) || val === noData))
return true;
return false;
}
const { r, g, b } = renderOptions ?? {};
const ranges = [r, g, b].map((item) => getRange(bands, item));
const redData = data[0];
const greenData = data[1];
const blueData = data[2];
for (let i = 0; i < data[0].length; i++) {
let red = decimal2rgb((redData[i] - ranges[0].min) / ranges[0].range);
let green = decimal2rgb((greenData[i] - ranges[1].min) / ranges[1].range);
let blue = decimal2rgb((blueData[i] - ranges[2].min) / ranges[2].range);
let alpha = ifNoDataFunc(redData[i], greenData[i], blueData[i]) ? 0 : 255;
colorMapping.map(([colorFrom, colorTo]) => {
if (red === colorFrom[0] && green === colorFrom[1] && blue === colorFrom[2]) {
red = colorTo[0];
green = colorTo[1];
blue = colorTo[2];
alpha = colorTo[3];
}
});
imageData[i * 4] = red;
imageData[i * 4 + 1] = green;
imageData[i * 4 + 2] = blue;
imageData[i * 4 + 3] = alpha;
}
const result = new ImageData(imageData, width, height);
return result;
}
function createCanavas(width, height) {
if ("OffscreenCanvas" in window) {
return new OffscreenCanvas(width, height);
} else {
const canv = document.createElement("canvas");
canv.width = width;
canv.height = height;
return canv;
}
}
var WorkerClass = null;
try {
var WorkerThreads =
typeof module !== 'undefined' && typeof module.require === 'function' && module.require('worker_threads') ||
typeof __non_webpack_require__ === 'function' && __non_webpack_require__('worker_threads') ||
typeof require === 'function' && require('worker_threads');
WorkerClass = WorkerThreads.Worker;
} catch(e) {} // eslint-disable-line
function decodeBase64$1(base64, enableUnicode) {
return Buffer.from(base64, 'base64').toString(enableUnicode ? 'utf16' : 'utf8');
}
function createBase64WorkerFactory$2(base64, sourcemapArg, enableUnicodeArg) {
var sourcemap = sourcemapArg === undefined ? null : sourcemapArg;
var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg;
var source = decodeBase64$1(base64, enableUnicode);
var start = source.indexOf('\n', 10) + 1;
var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : '');
return function WorkerFactory(options) {
return new WorkerClass(body, Object.assign({}, options, { eval: true }));
};
}
function decodeBase64(base64, enableUnicode) {
var binaryString = atob(base64);
if (enableUnicode) {
var binaryView = new Uint8Array(binaryString.length);
for (var i = 0, n = binaryString.length; i < n; ++i) {
binaryView[i] = binaryString.charCodeAt(i);
}
return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer));
}
return binaryString;
}
function createURL(base64, sourcemapArg, enableUnicodeArg) {
var sourcemap = sourcemapArg === undefined ? null : sourcemapArg;
var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg;
var source = decodeBase64(base64, enableUnicode);
var start = source.indexOf('\n', 10) + 1;
var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : '');
var blob = new Blob([body], { type: 'application/javascript' });
return URL.createObjectURL(blob);
}
function createBase64WorkerFactory$1(base64, sourcemapArg, enableUnicodeArg) {
var url;
return function WorkerFactory(options) {
url = url || createURL(base64, sourcemapArg, enableUnicodeArg);
return new Worker(url, options);
};
}
var kIsNodeJS = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
function isNodeJS() {
return kIsNodeJS;
}
function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {
if (isNodeJS()) {
return createBase64WorkerFactory$2(base64, sourcemapArg, enableUnicodeArg);
}
return createBase64WorkerFactory$1(base64, sourcemapArg, enableUnicodeArg);
}
var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgZnVuY3Rpb24gcmVzYW1wbGVOZWFyZXN0KGRhdGEsIG9wdGlvbnMpIHsKICAgIGNvbnN0IHsgc291cmNlV2lkdGgsIHNvdXJjZUhlaWdodCwgdGFyZ2V0V2lkdGgsIHRhcmdldEhlaWdodCwgd2luZG93LCBidWZmZXIgPSAwIH0gPSBvcHRpb25zOwogICAgY29uc3QgW3gwLCB5MCwgeDEsIHkxXSA9IHdpbmRvdzsKICAgIGNvbnN0IGVmZmVjdGl2ZVNvdXJjZVdpZHRoID0gc291cmNlV2lkdGggLSAyICogYnVmZmVyOwogICAgY29uc3QgZWZmZWN0aXZlU291cmNlSGVpZ2h0ID0gc291cmNlSGVpZ2h0IC0gMiAqIGJ1ZmZlcjsKICAgIGNvbnN0IHJlc2FtcGxlZERhdGEgPSBjb3B5TmV3U2l6ZShkYXRhLCB0YXJnZXRXaWR0aCwgdGFyZ2V0SGVpZ2h0KTsKICAgIGZvciAobGV0IHkgPSAwOyB5IDwgdGFyZ2V0SGVpZ2h0OyB5KyspIHsKICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCB0YXJnZXRXaWR0aDsgeCsrKSB7CiAgICAgICAgY29uc3QgY29sID0gYnVmZmVyICsgKGVmZmVjdGl2ZVNvdXJjZVdpZHRoICogKHgwICsgeCAvIHRhcmdldFdpZHRoICogKHgxIC0geDApKSA+Pj4gMCk7CiAgICAgICAgY29uc3Qgcm93ID0gYnVmZmVyICsgKGVmZmVjdGl2ZVNvdXJjZUhlaWdodCAqICh5MCArIHkgLyB0YXJnZXRIZWlnaHQgKiAoeTEgLSB5MCkpID4+PiAwKTsKICAgICAgICByZXNhbXBsZWREYXRhW3kgKiB0YXJnZXRXaWR0aCArIHhdID0gZGF0YVtyb3cgKiBzb3VyY2VXaWR0aCArIGNvbF07CiAgICAgIH0KICAgIH0KICAgIHJldHVybiByZXNhbXBsZWREYXRhOwogIH0KICBmdW5jdGlvbiByZXNhbXBsZURhdGEoZGF0YSwgb3B0aW9ucykgewogICAgY29uc3QgeyBtZXRob2QgPSAibmVhcmVzdCIgfSA9IG9wdGlvbnM7CiAgICBzd2l0Y2ggKG1ldGhvZCkgewogICAgICBjYXNlICJuZWFyZXN0IjoKICAgICAgICByZXR1cm4gcmVzYW1wbGVOZWFyZXN0KGRhdGEsIG9wdGlvbnMpOwogICAgICBjYXNlICJiaWxpbmVhciI6CiAgICAgICAgcmV0dXJuIHJlc2FtcGxlQmlsaW5lYXIoZGF0YSwgb3B0aW9ucyk7CiAgICB9CiAgfQogIGZ1bmN0aW9uIGNvcHlOZXdTaXplKGFycmF5LCB3aWR0aCwgaGVpZ2h0LCBzYW1wbGVzUGVyUGl4ZWwgPSAxKSB7CiAgICByZXR1cm4gbmV3IChPYmplY3QuZ2V0UHJvdG90eXBlT2YoYXJyYXkpKS5jb25zdHJ1Y3Rvcih3aWR0aCAqIGhlaWdodCAqIHNhbXBsZXNQZXJQaXhlbCk7CiAgfQogIGZ1bmN0aW9uIGxlcnAodjAsIHYxLCB0KSB7CiAgICByZXR1cm4gKDEgLSB0KSAqIHYwICsgdCAqIHYxOwogIH0KICBmdW5jdGlvbiByZXNhbXBsZUJpbGluZWFyKGRhdGEsIG9wdGlvbnMpIHsKICAgIGNvbnN0IHsgc291cmNlV2lkdGgsIHNvdXJjZUhlaWdodCwgdGFyZ2V0V2lkdGgsIHRhcmdldEhlaWdodCwgd2luZG93LCBidWZmZXIgPSAwLCBub2RhdGEgfSA9IG9wdGlvbnM7CiAgICBjb25zdCBbeDAsIHkwLCB4MSwgeTFdID0gd2luZG93OwogICAgY29uc3Qgd2luZG93V2lkdGggPSB4MSAtIHgwOwogICAgY29uc3Qgd2luZG93SGVpZ2h0ID0geTEgLSB5MDsKICAgIGNvbnN0IG5ld0FycmF5ID0gY29weU5ld1NpemUoZGF0YSwgdGFyZ2V0V2lkdGgsIHRhcmdldEhlaWdodCk7CiAgICBjb25zdCBlZmZlY3RpdmVTb3VyY2VXaWR0aCA9IHNvdXJjZVdpZHRoIC0gMiAqIGJ1ZmZlcjsKICAgIGNvbnN0IGVmZmVjdGl2ZVNvdXJjZUhlaWdodCA9IHNvdXJjZUhlaWdodCAtIDIgKiBidWZmZXI7CiAgICBjb25zdCBpbnZUYXJnZXRXaWR0aCA9IDEgLyB0YXJnZXRXaWR0aDsKICAgIGNvbnN0IGludlRhcmdldEhlaWdodCA9IDEgLyB0YXJnZXRIZWlnaHQ7CiAgICBjb25zdCBpc05vZGF0YSA9ICh2YWx1ZSkgPT4gewogICAgICBpZiAobm9kYXRhID09PSB2b2lkIDApCiAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICBpZiAobm9kYXRhID09PSAwKQogICAgICAgIHJldHVybiB2YWx1ZSA9PT0gMDsKICAgICAgcmV0dXJuIE1hdGguYWJzKCh2YWx1ZSAtIG5vZGF0YSkgLyBub2RhdGEpIDwgMWUtNjsKICAgIH07CiAgICBmb3IgKGxldCB5ID0gMDsgeSA8IHRhcmdldEhlaWdodDsgeSsrKSB7CiAgICAgIGNvbnN0IHlSYXRpbyA9IHkgKiBpbnZUYXJnZXRIZWlnaHQ7CiAgICAgIGNvbnN0IHlNYXBwZWQgPSB5MCArIHlSYXRpbyAqIHdpbmRvd0hlaWdodDsKICAgICAgY29uc3QgcmF3WSA9IGVmZmVjdGl2ZVNvdXJjZUhlaWdodCAqIHlNYXBwZWQgKyBidWZmZXI7CiAgICAgIGNvbnN0IHlsID0gTWF0aC5mbG9vcihyYXdZKTsKICAgICAgY29uc3QgeWggPSBNYXRoLm1pbihNYXRoLmNlaWwocmF3WSksIHNvdXJjZUhlaWdodCAtIGJ1ZmZlciAtIDEpOwogICAgICBjb25zdCB0eSA9IHJhd1kgLSB5bDsKICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCB0YXJnZXRXaWR0aDsgeCsrKSB7CiAgICAgICAgY29uc3QgeFJhdGlvID0geCAqIGludlRhcmdldFdpZHRoOwogICAgICAgIGNvbnN0IHhNYXBwZWQgPSB4MCArIHhSYXRpbyAqIHdpbmRvd1dpZHRoOwogICAgICAgIGNvbnN0IHJhd1ggPSBlZmZlY3RpdmVTb3VyY2VXaWR0aCAqIHhNYXBwZWQgKyBidWZmZXI7CiAgICAgICAgY29uc3QgeGwgPSBNYXRoLmZsb29yKHJhd1gpOwogICAgICAgIGNvbnN0IHhoID0gTWF0aC5taW4oTWF0aC5jZWlsKHJhd1gpLCBzb3VyY2VXaWR0aCAtIGJ1ZmZlciAtIDEpOwogICAgICAgIGNvbnN0IHR4RnJhY3Rpb24gPSByYXdYIC0geGw7CiAgICAgICAgY29uc3QgY2VudGVyID0gZGF0YVt5bCAqIHNvdXJjZVdpZHRoICsgeGxdOwogICAgICAgIGNvbnN0IG5laWdoYm9ycyA9IFsKICAgICAgICAgIGRhdGFbeWwgKiBzb3VyY2VXaWR0aCArIHhsXSwKICAgICAgICAgIGRhdGFbeWwgKiBzb3VyY2VXaWR0aCArIHhoXSwKICAgICAgICAgIGRhdGFbeWggKiBzb3VyY2VXaWR0aCArIHhsXSwKICAgICAgICAgIGRhdGFbeWggKiBzb3VyY2VXaWR0aCArIHhoXQogICAgICAgIF07CiAgICAgICAgbmVpZ2hib3JzLmZvckVhY2goKG5laWdoYm9yLCBpbmRleCkgPT4gewogICAgICAgICAgaWYgKGlzTm9kYXRhKG5laWdoYm9yKSkgewogICAgICAgICAgICBuZWlnaGJvcnNbaW5kZXhdID0gY2VudGVyOwogICAgICAgICAgfQogICAgICAgIH0pOwogICAgICAgIGlmIChpc05vZGF0YShjZW50ZXIpKSB7CiAgICAgICAgICBuZXdBcnJheVt5ICogdGFyZ2V0V2lkdGggKyB4XSA9IG5vZGF0YTsKICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KICAgICAgICBjb25zdCBbbGwsIGhsLCBsaCwgaGhdID0gbmVpZ2hib3JzOwogICAgICAgIGNvbnN0IHYwID0gbGVycChsbCwgaGwsIHR4RnJhY3Rpb24pOwogICAgICAgIGNvbnN0IHYxID0gbGVycChsaCwgaGgsIHR4RnJhY3Rpb24pOwogICAgICAgIGNvbnN0IHZhbHVlID0gbGVycCh2MCwgdjEsIHR5KTsKICAgICAgICBuZXdBcnJheVt5ICogdGFyZ2V0V2lkdGggKyB4XSA9IHZhbHVlOwogICAgICB9CiAgICB9CiAgICByZXR1cm4gbmV3QXJyYXk7CiAgfQoKICBvbm1lc3NhZ2UgPSBmdW5jdGlvbihlKSB7CiAgICBjb25zdCB7IGRhdGEsIG9wdGlvbnMsIGlkIH0gPSBlLmRhdGE7CiAgICBjb25zdCByZXN1bHQgPSByZXNhbXBsZURhdGEoZGF0YSwgb3B0aW9ucyk7CiAgICBwb3N0TWVzc2FnZSh7IGRhdGE6IHJlc3VsdCwgaWQgfSk7CiAgfTsKCn0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXdvcmtlci5qcy5tYXAKCg==', null, false);
/* eslint-enable */
const defaultPoolSize = typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 2 : 2;
class WorkerPool {
workers;
size;
messageId;
/**
* @constructor
* @param {Number} [size] The size of the pool. Defaults to the number of CPUs
* available. When this parameter is `null` or 0, then the
* decoding will be done in the main thread.
*/
constructor(size = defaultPoolSize) {
this.workers = null;
this.size = size ?? 0;
this.messageId = 0;
if (size) {
this.workers = [];
for (let i = 0; i < size; i++) {
this.workers.push({ worker: WorkerFactory(), idle: true });
}
}
}
async resample(data, options) {
return this.size === 0 ? resampleData(data, options) : new Promise((resolve) => {
const worker = this.workers.find((candidate) => candidate.idle) || this.workers[Math.floor(Math.random() * this.size)];
worker.idle = false;
const id = this.messageId++;
const onMessage = (e) => {
if (e.data.id === id) {
worker.idle = true;
resolve(e.data.data);
worker.worker.removeEventListener("message", onMessage);
}
};
worker.worker.addEventListener("message", onMessage);
worker.worker.postMessage({ data, options, id });
});
}
destroy() {
if (this.workers) {
this.workers.forEach((worker) => {
worker.worker.terminate();
});
this.workers = null;
}
}
}
const canvas = createCanavas(256, 256);
class TIFFImageryProvider {
constructor(options) {
this.options = options;
this.hasAlphaChannel = options.hasAlphaChannel ?? true;
this.maximumLevel = options.maximumLevel ?? 18;
this.minimumLevel = options.minimumLevel ?? 0;
this.credit = new Credit(options.credit || "", false);
this.errorEvent = new Event();
this._cacheSize = options.cacheSize ?? 100;
this.workerPool = new WorkerPool(options.workerPoolSize);
this.geotiffWorkerPool = new Pool(options.workerPoolSize);
this.ready = false;
if (defined(options.url)) {
this.readyPromise = this._build(options.url, options).then(() => {
return true;
});
}
}
ready;
tilingScheme;
rectangle;
tileSize;
tileWidth;
tileHeight;
maximumLevel;
minimumLevel;
credit;
errorEvent;
readyPromise;
bands;
noData;
hasAlphaChannel;
plot;
renderOptions;
readSamples;
requestLevels;
bbox;
_destroyed = false;
_source;
_imageCount;
_images = [];
_imagesCache = /* @__PURE__ */ new Map();
_cacheSize;
_isTiled;
_proj;
origin;
reverseY = false;
samples;
workerPool;
geotiffWorkerPool;
get isDestroyed() {
return this._destroyed;
}
async _build(url, options = {}) {
var _a, _b;
const { tileSize, renderOptions, projFunc, requestOptions } = options;
let source = await (url instanceof File || url instanceof Blob ? fromBlob(url) : fromUrl(url, requestOptions));
let image = await source.getImage();
this._isTiled = image.isTiled;
if (!this._isTiled && typeof url === "string") {
source = await fromBlob(await (await fetch(url)).blob());
image = await source.getImage();
}
this._source = source;
this.origin = this._getOrigin(image);
this.bbox = image.getBoundingBox();
this.reverseY = this._checkIfReversed(image);
const [west, south, east, north] = this.bbox;
const prjCode = +(image.geoKeys.ProjectedCSTypeGeoKey ?? image.geoKeys.GeographicTypeGeoKey);
this._proj = projFunc == null ? void 0 : projFunc(prjCode);
if (prjCode === 3857 || prjCode === 900913) {
this.tilingScheme = new WebMercatorTilingScheme({
rectangleNortheastInMeters: new Cartesian2(east, north),
rectangleSouthwestInMeters: new Cartesian2(west, south)
});
} else if (prjCode === 4326) {
this.tilingScheme = new GeographicTilingScheme({
rectangle: Rectangle.fromDegrees(...this.bbox),
numberOfLevelZeroTilesX: 1,
numberOfLevelZeroTilesY: 1
});
} else if (typeof ((_a = this._proj) == null ? void 0 : _a.project) === "function" && typeof ((_b = this._proj) == null ? void 0 : _b.unproject) === "function") {
console.warn(`[Experimental] Reprojection EPSG:${prjCode}`);
this.tilingScheme = new TIFFImageryProviderTilingScheme({
rectangleNortheastInMeters: new Cartesian2(east, north),
rectangleSouthwestInMeters: new Cartesian2(west, south),
...this._proj
});
} else {
const error = new DeveloperError(`Unspported projection type: EPSG:${prjCode}, please add projFunc parameter to handle projection`);
throw error;
}
this.rectangle = this.tilingScheme.rectangle;
if (this.rectangle.east < this.rectangle.west) {
this.rectangle.east += Math$1.TWO_PI;
}
this._imageCount = await source.getImageCount();
this.tileSize = this.tileWidth = tileSize || (this._isTiled ? image.getTileWidth() : image.getWidth()) || 256;
this.tileHeight = tileSize || (this._isTiled ? image.getTileHeight() : image.getHeight()) || 256;
console.log(this.tileWidth, this.tileHeight);
this.requestLevels = this._isTiled ? await this._getCogLevels() : [0];
this._images = new Array(this._imageCount).fill(null);
const samples = image.getSamplesPerPixel();
this.samples = samples;
this.renderOptions = renderOptions ?? {};
const noData = image.getGDALNoData();
this.noData = this.renderOptions.nodata ?? noData;
if (samples < 3 && this.renderOptions.convertToRGB) {
const error = new DeveloperError("Can not render the image as RGB, please check the convertToRGB parameter");
throw error;
}
if (!this.renderOptions.single && !this.renderOptions.multi && !this.renderOptions.convertToRGB) {
if (samples > 2) {
this.renderOptions = {
convertToRGB: true,
...this.renderOptions
};
} else {
this.renderOptions = {
single: {
band: 1
},
...this.renderOptions
};
}
}
if (this.renderOptions.single) {
this.renderOptions.single.band = this.renderOptions.single.band ?? 1;
}
const { single, multi, convertToRGB } = this.renderOptions;
this.readSamples = multi ? [multi.r.band - 1, multi.g.band - 1, multi.b.band - 1] : convertToRGB ? [0, 1, 2] : [single.band - 1];
if (single == null ? void 0 : single.expression) {
this.readSamples = findAndSortBandNumbers(single.expression);
}
const bands = {};
await Promise.all(this.readSamples.map(async (i) => {
const element = image.getGDALMetadata(i);
const bandNum = i + 1;
if ((element == null ? void 0 : element.STATISTICS_MINIMUM) && (element == null ? void 0 : element.STATISTICS_MAXIMUM)) {
bands[bandNum] = {
min: +element.STATISTICS_MINIMUM,
max: +element.STATISTICS_MAXIMUM
};
} else {
if (convertToRGB) {
bands[bandNum] = {
min: 0,
max: 255
};
}
if (multi) {
const inputBand = multi[Object.keys(multi).find((key) => {
var _a2;
return ((_a2 = multi[key]) == null ? void 0 : _a2.band) === bandNum;
})];
if ((inputBand == null ? void 0 : inputBand.min) !== void 0 && (inputBand == null ? void 0 : inputBand.max) !== void 0) {
const { min, max } = inputBand;
bands[bandNum] = {
min,
max
};
}
}
if (single && !single.expression && single.band === bandNum && single.domain) {
bands[bandNum] = {
min: single.domain[0],
max: single.domain[1]
};
}
if (!(single == null ? void 0 : single.expression) && !bands[bandNum]) {
console.warn(`Can not get band${bandNum} min/max, try to calculate min/max values, or setting ${single ? "domain" : "min / max"}`);
const previewImage = await source.getImage(this.requestLevels[0]);
const data = (await previewImage.readRasters({
samples: [i],
pool: this.geotiffWorkerPool
}))[0].filter((item) => !isNaN(item));
bands[bandNum] = getMinMax(data, noData);
}
}
}));
this.bands = bands;
try {
if (this.renderOptions.single) {
const band = this.bands[single.band];
if (!single.expression && !band) {
throw new DeveloperError(`Invalid band${single.band}`);
}
const domain = single.domain ?? [band.min, band.max];
this.plot = new plot({
canvas,
...single,
domain
});
this.plot.setNoDataValue(this.noData);
const { expression, colors, colorScaleImage } = single;
this.plot.setExpression(expression);
if (colors) {
const colorScale = generateColorScale(colors, (single == null ? void 0 : single.useRealValue) ? domain : [0, 1]);
addColorScale("temp", colorScale.colors, colorScale.positions);
this.plot.setColorScale("temp");
} else if (!colorScaleImage) {
this.plot.setColorScale((single == null ? void 0 : single.colorScale) ?? "blackwhite");
}
}
} catch (e) {
console.error(e);
this.errorEvent.raiseEvent(e);
}
this.readyPromise = Promise.resolve(true);
this.ready = true;
}
static async fromUrl(url, options = {}) {
const provider = new TIFFImageryProvider(options);
await provider._build(url, {
...options,
url: void 0
});
return provider;
}
/**
* Get the origin of an image. If the image does not have an affine transform,
* the top-left corner of the pixel bounds is returned.
* @param {GeoTIFFImage} image The image.
* @return {Array<number>} The image origin.
*/
_getOrigin(image) {
try {
return image.getOrigin().slice(0, 2);
} catch (_) {
return [0, image.fileDirectory.ImageLength];
}
}
_checkIfReversed(image) {
const pixelScale = image.getFileDirectory().ModelPixelScale;
if (pixelScale) {
const pixelScaleY = pixelScale[1];
if (pixelScaleY < 0)
return true;
}
const transformation = image.getFileDirectory().ModelTransformation;
if (transformation) {
const originX = transformation[3];
const originY = transformation[7];
if (originY > originX)
return true;
}
return false;
}
/**
* get suitable cog levels
*/
async _getCogLevels() {
const levels = [];
let maximumLevel = this._imageCount - 1;
for (let i = this._imageCount - 1; i >= 0; i--) {
const image = this._images[i] = await this._source.getImage(i);
const width = image.getWidth();
const height = image.getHeight();
const size = Math.max(width, height);
if (i === this._imageCount - 1) {
const firstImageLevel = Math.ceil((size - this.tileSize) / this.tileSize);
levels.push(...new Array(firstImageLevel).fill(i));
}
if (size > this.tileSize * 0.5) {
maximumLevel = i;
break;
}
}
let nowCogLevel = maximumLevel;
while (nowCogLevel >= 0) {
levels.push(nowCogLevel--);
}
return levels;
}
/**
* Get tile data
* @param x
* @param y
* @param z
*/
async _loadTile(reqx, reqy, reqz) {
var _a;
let x = reqx, y = reqy, z = reqz, startX = reqx, startY = reqy;
const maxCogLevel = this.requestLevels.length - 1;
if (z > maxCogLevel) {
z = maxCogLevel;
x = x >> reqz - maxCogLevel;
y = y >> reqz - maxCogLevel;
startX = x << reqz - z;
startY = y << reqz - z;
}
const index = this.requestLevels[z];
let image = this._images[index];
if (!image) {
image = this._images[index] = await this._source.getImage(index);
}
const width = image.getWidth();
const height = image.getHeight();
const tileXNum = this.tilingScheme.getNumberOfXTilesAtLevel(z);
const tileYNum = this.tilingScheme.getNumberOfYTilesAtLevel(z);
const tilePixel = {
xWidth: width / tileXNum,
yWidth: height / tileYNum
};
let window = [
Math.round(x * tilePixel.xWidth),
Math.round(y * tilePixel.yWidth),
Math.round((x + 1) * tilePixel.xWidth),
Math.round((y + 1) * tilePixel.yWidth)
];
if (this._proj && this.tilingScheme instanceof TIFFImageryProviderTilingScheme) {
const targetRect = this.tilingScheme.tileXYToNativeRectangle2(x, y, z);
const nativeRect = this.tilingScheme.nativeRectangle;
targetRect.west -= nativeRect.width / width;
targetRect.east += nativeRect.width / width;
targetRect.south -= nativeRect.height / height;
targetRect.north += nativeRect.height / height;
window = [
~~((targetRect.west - nativeRect.west) / nativeRect.width * width),
~~((nativeRect.north - targetRect.north) / nativeRect.height * height),
~~((targetRect.east - nativeRect.west) / nativeRect.width * width),
~~((nativeRect.north - targetRect.south) / nativeRect.height * height)
];
}
if (this.reverseY) {
window = [window[0], height - window[3], window[2], height - window[1]];
}
const buffer = 1;
window = [window[0] - buffer, window[1] - buffer, window[2] + buffer, window[3] + buffer];
const sourceWidth = window[2] - window[0], sourceHeight = window[3] - window[1];
const options = {
window,
pool: this.geotiffWorkerPool,
samples: this.readSamples,
fillValue: this.noData,
interleave: false
};
let res;
try {
if (this.renderOptions.convertToRGB) {
res = await image.readRGB(options);
} else {
res = await image.readRasters(options);
if (this.reverseY) {
res = await Promise.all(res.map((array) => reverseArray({ array, width: res.width, height: res.height })));
}
}
if (((_a = this._proj) == null ? void 0 : _a.project) && this.tilingScheme instanceof TIFFImageryProviderTilingScheme) {
const sourceRect = this.tilingScheme.tileXYToNativeRectangle2(x, y, z);
const targetRect = this.tilingScheme.tileXYToRectangle(x, y, z);
const sourceBBox = [sourceRect.west, sourceRect.south, sourceRect.east, sourceRect.north];
const targetBBox = [targetRect.west, targetRect.south, targetRect.east, targetRect.north].map(Math$1.toDegrees);
const result = [];
for (let i = 0; i < res.length; i++) {
const prjData = reprojection({
data: res[i],
sourceWidth,
sourceHeight,
nodata: this.noData,
project: this._proj.project,
sourceBBox,
targetBBox
});
result.push(prjData);
}
res = result;
}
const tileNum = 1 << reqz - z;
const x0 = (reqx - startX) / tileNum;
const y0 = (reqy - startY) / tileNum;
const step = 1 / (1 << reqz - z);
const x1 = x0 + step;
const y1 = y0 + step;
res = await Promise.all(res.map(async (data) => this.workerPool.resample(data, {
sourceWidth,
sourceHeight,
targetWidth: this.tileWidth,
targetHeight: this.tileHeight,
window: [x0, y0, x1, y1],
method: this.renderOptions.resampleMethod,
buffer,
nodata: this.noData
})));
return {
data: res,
width: this.tileWidth,
height: this.tileHeight
};
} catch (error) {
this.errorEvent.raiseEvent(error);
throw error;
}
}
async requestImage(x, y, z) {
if (!this.ready) {
throw new DeveloperError(
"requestImage must not be called before the imagery provider is ready."
);
}
if (z < this.minimumLevel || z > this.maximumLevel)
return void 0;
const cacheKey = `${x}_${y}_${z}`;
if (this._imagesCache.has(cacheKey)) {
return this._imagesCache.get(cacheKey);
}
const { single, multi, convertToRGB } = this.renderOptions;
try {
const { width, height, data } = await this._loadTile(x, y, z);
if (this._destroyed || !width || !height) {
return void 0;
}
let result;
if (multi || convertToRGB) {
const opts = {
data,
width,
height,
renderOptions: multi ?? ["r", "g", "b"].reduce((pre, val, index) => ({
...pre,
[val]: {
band: index + 1,
min: 0,
max: 255
}
}), {}),
bands: this.bands,
noData: this.noData,
colorMapping: Object.entries(this.renderOptions.colorMapping ?? { "black": "transparent" }).map((val) => val.map(stringColorToRgba))
};
result = await generateImage(opts);
} else if (single && this.plot) {
const { band = 1 } = single;
this.plot.removeAllDataset();
this.readSamples.forEach((sample, index) => {
this.plot.addDataset(`b${sample + 1}`, data[index], width, height);
});
if (single.expression) {
this.plot.render();
} else {
this.plot.renderDataset(`b${band}`);
}
const canv = createCanavas(this.tileWidth, this.tileHeight);
const ctx = canv.getContext("2d");
ctx.drawImage(this.plot.canvas, 0, 0);
result = canv;
}
if (result) {
if (this._imagesCache.size >= this._cacheSize) {
const oldestKey = this._imagesCache.keys().next().value;
this._imagesCache.delete(oldestKey);
}
this._imagesCache.set(cacheKey, result);
}
return result;
} catch (e) {
console.error(e);
this.errorEvent.raiseEvent(e);
throw e;
}
}
async pickFeatures(x, y, zoom, longitude, latitude) {
if (!this.options.enablePickFeatures)
return void 0;
const z = zoom > this.maximumLevel ? this.maximumLevel : zoom;
const index = this.requestLevels[z];
let image = this._images[index];
if (!image) {
image = this._images[index] = await this._source.getImage(index);
}
const width = image.getWidth();
const height = image.getHeight();
let posX, posY, window;
const { west, south, north, width: lonWidth } = this.rectangle;
let lonGap = longitude - west;
if (longitude < west) {
lonGap += Math$1.TWO_PI;
}
posX = ~~(Math.abs(lonGap / lonWidth) * width);
posY = ~~(Math.abs((north - latitude) / (north - south)) * height);
window = [posX, posY, posX + 1, posY + 1];
if (this.reverseY) {
posY = height - posY;
window = [posX, posY - 1, posX + 1, posY];
}
const options = {
window,
height: 1,
width: 1,
pool: this.geotiffWorkerPool,
interleave: false
};
let res;
if (this.renderOptions.convertToRGB) {
res = await image.readRGB(options);
} else {
res = await image.readRasters(options);
}
const featureInfo = new ImageryLayerFeatureInfo();
featureInfo.name = `lon:${(longitude / Math.PI * 180).toFixed(6)}, lat:${(latitude / Math.PI * 180).toFixed(6)}`;
const data = {};
res == null ? void 0 : res.forEach((item, index2) => {
data[index2] = item == null ? void 0 : item[0];
});
featureInfo.data = data;
if (res) {
featureInfo.configureDescriptionFromProperties(data);
}
return [featureInfo];
}
destroy() {
var _a;
if (this._imagesCache) {
for (const key in this._imagesCache) {
delete this._imagesCache[key];
}
this._imagesCache.clear();
}
if (this.workerPool) {
this.workerPool.destroy();
}
if (this.plot && this.plot.gl) {
for (const programKey in this.plot.programCache) {
this.plot.gl.deleteProgram(this.plot.programCache[programKey]);
}
this.plot.gl.deleteBuffer(this.plot.positionBuffer);
}
(_a = this.plot) == null ? void 0 : _a.destroy();
this._images = [];
this._source = void 0;
this._destroyed = true;
}
}
export { TIFFImageryProvider, colorscales, TIFFImageryProvider as default, renderColorScaleToCanvas };
//# sourceMappingURL=index.js.map