Compare commits
15 Commits
0.0.3
...
1af4afc7c6
| Author | SHA1 | Date | |
|---|---|---|---|
| 1af4afc7c6 | |||
| a3efe939e8 | |||
| 5daee1a01a | |||
| 2d5d305528 | |||
| 8da46c0511 | |||
| 75b5d7708c | |||
| 5b2177a795 | |||
| 8cb33dca5f | |||
| ae2e0af18a | |||
| 2ac5a3d4f0 | |||
| 5f78a1afde | |||
| ef5cf78a03 | |||
| b60e62af70 | |||
| 3e59fe1462 | |||
| 4b4bf5ed65 |
@@ -1,3 +1,3 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("0.0.3")]
|
||||
[assembly: AssemblyVersion("0.0.6")]
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using Cryville.EEW.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Cryville.EEW.Unity {
|
||||
@@ -8,11 +12,18 @@ namespace Cryville.EEW.Unity {
|
||||
float SeverityColorMappingLuminanceMultiplier,
|
||||
bool UseContinuousColor,
|
||||
string ColorScheme,
|
||||
string LocationNamer,
|
||||
|
||||
string OverrideTimeZone,
|
||||
bool DoDisplayTimeZone,
|
||||
bool DoSwitchBackToHistory,
|
||||
|
||||
string NowcastWarningDelayTolerance,
|
||||
|
||||
string OverrideDisplayCulture,
|
||||
IReadOnlyCollection<TTSCultureConfig> TTSCultures,
|
||||
bool DoIgnoreLanguageVariant,
|
||||
|
||||
IReadOnlyCollection<EventSourceConfig> EventSources
|
||||
) {
|
||||
public static Config Default => new(
|
||||
@@ -20,11 +31,18 @@ namespace Cryville.EEW.Unity {
|
||||
1f,
|
||||
false,
|
||||
"Default",
|
||||
"FERegionLong",
|
||||
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
|
||||
"1:00:00",
|
||||
|
||||
"",
|
||||
new List<TTSCultureConfig> { new(SharedCultures.CurrentUICulture) },
|
||||
true,
|
||||
|
||||
new List<EventSourceConfig> {
|
||||
new JMAAtomEventSourceConfig(Array.Empty<string>()),
|
||||
new UpdateCheckerEventSourceConfig(),
|
||||
@@ -54,9 +72,23 @@ namespace Cryville.EEW.Unity {
|
||||
record NOAAEventSourceConfig([property: JsonRequired] string Subtype) : EventSourceConfig;
|
||||
record UpdateCheckerEventSourceConfig : EventSourceConfig;
|
||||
record USGSQuakeMLEventSourceConfig([property: JsonRequired] string Subtype) : EventSourceConfig;
|
||||
record WolfxEventSourceConfig(IReadOnlyCollection<string> Filter = null, bool IsFilterWhitelist = false) : EventSourceConfig;
|
||||
record WolfxEventSourceConfig(IReadOnlyCollection<string> Filter = null, bool IsFilterWhitelist = false, bool UseRawCENCLocationName = false) : EventSourceConfig;
|
||||
|
||||
[JsonSerializable(typeof(Config))]
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSourceGenerationOptions(Converters = new Type[] { typeof(CultureInfoConverter) }, WriteIndented = true)]
|
||||
sealed partial class ConfigSerializationContext : JsonSerializerContext { }
|
||||
|
||||
sealed class CultureInfoConverter : JsonConverter<CultureInfo> {
|
||||
public override CultureInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
||||
Debug.Assert(typeToConvert == typeof(CultureInfo));
|
||||
var value = reader.GetString();
|
||||
if (value == null) return CultureInfo.InvariantCulture;
|
||||
if (value == "") return SharedCultures.CurrentUICulture;
|
||||
return SharedCultures.Get(value);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, CultureInfo value, JsonSerializerOptions options) {
|
||||
writer.WriteStringValue(value.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:b92f9c7ac10b1c04e86fc48210f62ab1",
|
||||
"GUID:1e0937e40dadba24a97b7342c4559580",
|
||||
"GUID:e5b7e7f40a80a814ba706299d68f9213",
|
||||
"GUID:da293eebbcb9a4947a212534c52d1a32"
|
||||
],
|
||||
|
||||
@@ -17,19 +17,24 @@ namespace Cryville.EEW.Unity.Map {
|
||||
GameObject m_prefabTile;
|
||||
[SerializeField]
|
||||
GameObject m_prefabBitmapHolder;
|
||||
[SerializeField]
|
||||
int m_maxMapTileZoom = 10;
|
||||
[SerializeField]
|
||||
bool m_isEditor;
|
||||
|
||||
readonly MapTileCacheManager _tiles = new();
|
||||
MapTileCacheManager _tiles;
|
||||
float _elementLayerZ;
|
||||
|
||||
void Start() {
|
||||
_camera = GetComponent<Camera>();
|
||||
_tiles = m_isEditor ? new EditorMapTileCacheManager() : new MapTileCacheManager();
|
||||
_tiles.ExtraCachedZoomLevel = 20;
|
||||
_tiles.Parent = m_layerTile;
|
||||
_tiles.PrefabTile = m_prefabTile;
|
||||
_tiles.PrefabBitmapHolder = m_prefabBitmapHolder;
|
||||
_tiles.CacheDir = Application.temporaryCachePath;
|
||||
_camera.orthographicSize = 0.5f / MathF.Max(1, (float)_camera.pixelWidth / _camera.pixelHeight);
|
||||
_elementLayerZ = m_layerElement.transform.position.z;
|
||||
if (m_layerElement != null) _elementLayerZ = m_layerElement.transform.position.z;
|
||||
_mapElementUpdated = true;
|
||||
}
|
||||
void OnDestroy() {
|
||||
@@ -74,7 +79,7 @@ namespace Cryville.EEW.Unity.Map {
|
||||
}
|
||||
}
|
||||
void ZoomToMapElement() {
|
||||
var aabb = m_layerElement.AABB;
|
||||
var aabb = m_layerElement != null ? m_layerElement.AABB : null;
|
||||
if (aabb is not RectangleF b) return;
|
||||
if (b.Width * _camera.pixelHeight < _camera.pixelWidth * b.Height)
|
||||
Scale = b.Height;
|
||||
@@ -97,26 +102,30 @@ namespace Cryville.EEW.Unity.Map {
|
||||
transform.localPosition = new(nx, Math.Clamp(transform.position.y, h / 2 - 1, -h / 2), -20);
|
||||
|
||||
var bounds = new Bounds((Vector2)transform.position, new Vector2(w, h));
|
||||
int zoom = Math.Clamp((int)Math.Log(vz / 256, 2) + 1, 0, 10);
|
||||
int zoom = Math.Clamp((int)Math.Log(vz / 256, 2) + 1, 0, m_maxMapTileZoom);
|
||||
int zoomScale = 1 << zoom;
|
||||
_tiles.MoveTo(
|
||||
new(Mathf.FloorToInt(bounds.min.x * zoomScale), Mathf.FloorToInt(-bounds.max.y * zoomScale), zoom),
|
||||
new(Mathf.CeilToInt(bounds.max.x * zoomScale), Mathf.CeilToInt(-bounds.min.y * zoomScale), zoom)
|
||||
);
|
||||
|
||||
m_layerElement.Scale = h;
|
||||
m_layerElementSub.Scale = h;
|
||||
if (m_layerElement != null) {
|
||||
m_layerElement.Scale = h;
|
||||
}
|
||||
if (m_layerElementSub != null) {
|
||||
m_layerElementSub.Scale = h;
|
||||
|
||||
if (nx - w / 2 < 0) {
|
||||
m_layerElementSub.gameObject.SetActive(true);
|
||||
m_layerElementSub.transform.localPosition = new(-1, 0, _elementLayerZ);
|
||||
}
|
||||
else if (nx + w / 2 > 1) {
|
||||
m_layerElementSub.gameObject.SetActive(true);
|
||||
m_layerElementSub.transform.localPosition = new(1, 0, _elementLayerZ);
|
||||
}
|
||||
else {
|
||||
m_layerElementSub.gameObject.SetActive(false);
|
||||
if (nx - w / 2 < 0) {
|
||||
m_layerElementSub.gameObject.SetActive(true);
|
||||
m_layerElementSub.transform.localPosition = new(-1, 0, _elementLayerZ);
|
||||
}
|
||||
else if (nx + w / 2 > 1) {
|
||||
m_layerElementSub.gameObject.SetActive(true);
|
||||
m_layerElementSub.transform.localPosition = new(1, 0, _elementLayerZ);
|
||||
}
|
||||
else {
|
||||
m_layerElementSub.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
Assets/Cryville.EEW.Unity/Map/EditorMapTileCacheManager.cs
Normal file
15
Assets/Cryville.EEW.Unity/Map/EditorMapTileCacheManager.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Cryville.EEW.Core.Map;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cryville.EEW.Unity.Map {
|
||||
sealed class EditorMapTileCacheManager : MapTileCacheManager {
|
||||
protected override MapTileBitmapHolder CreateBitmapHolder(MapTileIndex index) => new(
|
||||
index,
|
||||
GameObject.Instantiate(PrefabBitmapHolder, Parent, false),
|
||||
new($"https://tile.openstreetmap.org/{index.Z}/{index.NX}/{index.NY}.png")
|
||||
);
|
||||
|
||||
protected override string GetCacheFilePath(MapTileIndex index) => Path.Combine(CacheDir, $"map_editor/{index.Z}/{index.NX}/{index.NY}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 478198b8ecc0082449fa3f68795174a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -7,10 +7,12 @@ using UnityEngine;
|
||||
|
||||
namespace Cryville.EEW.Unity.Map {
|
||||
sealed class MapTileBitmapHolder : Core.Map.MapTileBitmapHolder {
|
||||
MapTileBitmapHolderBehaviour _behaviour;
|
||||
readonly MapTileBitmapHolderBehaviour _behaviour;
|
||||
readonly Uri _uri;
|
||||
|
||||
public MapTileBitmapHolder(MapTileIndex index, GameObject gameObject) : base(index) {
|
||||
public MapTileBitmapHolder(MapTileIndex index, GameObject gameObject, Uri uri) : base(index) {
|
||||
_behaviour = gameObject.GetComponent<MapTileBitmapHolderBehaviour>();
|
||||
_uri = uri;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
@@ -20,8 +22,7 @@ namespace Cryville.EEW.Unity.Map {
|
||||
}
|
||||
}
|
||||
|
||||
protected override Uri GetUri() =>
|
||||
new($"https://server.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{Index.Z}/{Index.NY}/{Index.NX}");
|
||||
protected override Uri GetUri() => _uri;
|
||||
|
||||
protected override Task LoadBitmap(FileInfo file, CancellationToken cancellationToken) {
|
||||
_behaviour.Load(file);
|
||||
|
||||
@@ -45,13 +45,13 @@ namespace Cryville.EEW.Unity.Map {
|
||||
_isReady = false;
|
||||
}
|
||||
if (_req == null || !_req.isDone) return;
|
||||
if (_texHandler.isDone) {
|
||||
if (_texHandler.isDone && _texHandler.texture != null) {
|
||||
_tex = _texHandler.texture;
|
||||
_tex.wrapMode = TextureWrapMode.Clamp;
|
||||
_sprite = Sprite.Create(_tex, new Rect(0, 0, _tex.width, _tex.height), Vector2.zero, _tex.height, 0, SpriteMeshType.FullRect, Vector4.zero, false);
|
||||
}
|
||||
else {
|
||||
Debug.LogError(_req.error);
|
||||
Debug.LogError(_texHandler.error);
|
||||
_localFile.Delete();
|
||||
}
|
||||
_req.Dispose();
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cryville.EEW.Unity.Map {
|
||||
sealed class MapTileCacheManager : MapTileCacheManager<MapTileBitmapHolder> {
|
||||
class MapTileCacheManager : MapTileCacheManager<MapTileBitmapHolder> {
|
||||
public GameObject PrefabTile { get; set; }
|
||||
public GameObject PrefabBitmapHolder { get; set; }
|
||||
|
||||
@@ -12,8 +12,12 @@ namespace Cryville.EEW.Unity.Map {
|
||||
|
||||
public string CacheDir { get; set; }
|
||||
|
||||
protected override MapTileBitmapHolder CreateBitmapHolder(MapTileIndex index) => new(index, GameObject.Instantiate(PrefabBitmapHolder, Parent, false));
|
||||
|
||||
protected override MapTileBitmapHolder CreateBitmapHolder(MapTileIndex index) => new(
|
||||
index,
|
||||
GameObject.Instantiate(PrefabBitmapHolder, Parent, false),
|
||||
new($"https://server.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{index.Z}/{index.NY}/{index.NX}")
|
||||
);
|
||||
|
||||
protected override string GetCacheFilePath(MapTileIndex index) => Path.Combine(CacheDir, $"map/{index.Z}/{index.NX}/{index.NY}");
|
||||
|
||||
readonly Dictionary<MapTile<MapTileBitmapHolder>, MapTile> _map = new();
|
||||
|
||||
219
Assets/Cryville.EEW.Unity/Map/RegionEditor.cs
Normal file
219
Assets/Cryville.EEW.Unity/Map/RegionEditor.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cryville.EEW.Unity.Map {
|
||||
sealed class RegionEditor : MonoBehaviour {
|
||||
QuadTreeNode _root;
|
||||
|
||||
[SerializeField] CameraController m_cameraController;
|
||||
[SerializeField] GameObject m_regionViewPrefab;
|
||||
|
||||
[SerializeField] TMP_Text m_textSelectedInfo;
|
||||
[SerializeField] TMP_Text m_textHoveredInfo;
|
||||
[SerializeField] TMP_InputField m_inputId;
|
||||
|
||||
readonly Dictionary<QuadTreeNode, RegionView> _map = new();
|
||||
|
||||
void Start() {
|
||||
var file = new FileInfo(Path.Combine(Application.persistentDataPath, "regions.json"));
|
||||
if (file.Exists) {
|
||||
using var stream = file.OpenRead();
|
||||
_root = JsonSerializer.Deserialize<QuadTreeNode>(stream);
|
||||
}
|
||||
else {
|
||||
_root = NewNode();
|
||||
}
|
||||
BuildView(_root);
|
||||
}
|
||||
|
||||
public void Save() {
|
||||
var file = new FileInfo(Path.Combine(Application.persistentDataPath, "regions.json"));
|
||||
using var stream = file.Open(FileMode.Create);
|
||||
JsonSerializer.Serialize(stream, _root);
|
||||
}
|
||||
|
||||
void BuildView(QuadTreeNode node) {
|
||||
var view = Instantiate(m_regionViewPrefab, transform, false).GetComponent<RegionView>();
|
||||
view.Init(node.X, node.Y, node.Z);
|
||||
view.Id = node.Data.Id;
|
||||
view.IsLeaf = node.Children == null;
|
||||
_map.Add(node, view);
|
||||
BuildChildViews(node);
|
||||
}
|
||||
|
||||
void BuildChildViews(QuadTreeNode node) {
|
||||
if (node.Children == null) return;
|
||||
foreach (var child in node.Children) {
|
||||
BuildView(child);
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyChildViews(QuadTreeNode node) {
|
||||
if (node.Children == null) return;
|
||||
foreach (var child in node.Children) {
|
||||
Destroy(_map[child].gameObject);
|
||||
_map.Remove(child);
|
||||
}
|
||||
}
|
||||
|
||||
QuadTreeNode _hoveredNode;
|
||||
QuadTreeNode _selectedNode;
|
||||
Vector3? _ppos;
|
||||
void Update() {
|
||||
var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
pos.y += 1;
|
||||
var hoveredNode = _root.Get(pos);
|
||||
if (hoveredNode != _hoveredNode) {
|
||||
HoverNode(hoveredNode);
|
||||
}
|
||||
if (Input.GetMouseButtonDown(0)) {
|
||||
_ppos = Input.mousePosition;
|
||||
}
|
||||
if (Input.GetMouseButton(0) && _ppos is Vector3 pos0) {
|
||||
if (Input.mousePosition != pos0) {
|
||||
_ppos = null;
|
||||
}
|
||||
}
|
||||
if (hoveredNode == null) return;
|
||||
if (Input.GetMouseButtonUp(0) && _ppos != null) {
|
||||
SelectNode(hoveredNode);
|
||||
_ppos = null;
|
||||
}
|
||||
if (m_inputId.isFocused)
|
||||
return;
|
||||
if (Input.GetKeyUp(KeyCode.A)) {
|
||||
MergeNode(hoveredNode);
|
||||
}
|
||||
if (Input.GetKeyUp(KeyCode.S)) {
|
||||
SplitNode(hoveredNode);
|
||||
}
|
||||
if (Input.GetKeyUp(KeyCode.C)) {
|
||||
m_inputId.text = hoveredNode.Data.Id;
|
||||
}
|
||||
if (Input.GetKeyUp(KeyCode.V)) {
|
||||
hoveredNode.Data.Id = m_inputId.text;
|
||||
_map[hoveredNode].Id = hoveredNode.Data.Id;
|
||||
}
|
||||
}
|
||||
void HoverNode(QuadTreeNode node) {
|
||||
if (_hoveredNode != null) {
|
||||
_map[_hoveredNode].IsHovered = false;
|
||||
}
|
||||
_hoveredNode = node;
|
||||
if (_hoveredNode != null) {
|
||||
_map[_hoveredNode].IsHovered = true;
|
||||
m_textHoveredInfo.text = string.Format(CultureInfo.InvariantCulture, "<Hovered>\nZ: {2}, XY: ({0}, {1})\nD: {3}", node.X, node.Y, node.Z, node.Data.Id);
|
||||
}
|
||||
else {
|
||||
m_textHoveredInfo.text = "";
|
||||
}
|
||||
}
|
||||
void SelectNode(QuadTreeNode node) {
|
||||
if (_selectedNode != null) {
|
||||
_map[_selectedNode].IsSelected = false;
|
||||
}
|
||||
_selectedNode = node;
|
||||
if (_selectedNode != null) {
|
||||
_map[_selectedNode].IsSelected = true;
|
||||
m_textSelectedInfo.text = string.Format(CultureInfo.InvariantCulture, "<Selected>\nZ: {2}, XY: ({0}, {1})\nD: {3}", node.X, node.Y, node.Z, node.Data.Id);
|
||||
}
|
||||
else {
|
||||
m_textSelectedInfo.text = "";
|
||||
}
|
||||
}
|
||||
void MergeNode(QuadTreeNode node) {
|
||||
var parent = node.Parent;
|
||||
if (parent == null)
|
||||
return;
|
||||
DestroyChildViews(parent);
|
||||
_map[parent].IsLeaf = true;
|
||||
parent.Merge();
|
||||
_hoveredNode = null;
|
||||
if (_selectedNode != null && !_map.ContainsKey(_selectedNode)) {
|
||||
_selectedNode = null;
|
||||
}
|
||||
}
|
||||
void SplitNode(QuadTreeNode node) {
|
||||
node.Split();
|
||||
_map[node].IsLeaf = false;
|
||||
BuildChildViews(node);
|
||||
}
|
||||
|
||||
static QuadTreeNode NewNode() => new() { Data = new("") };
|
||||
|
||||
sealed class QuadTreeNode {
|
||||
QuadTreeNode[] m_children;
|
||||
public QuadTreeNode[] Children {
|
||||
get => m_children;
|
||||
set {
|
||||
if (m_children != null) {
|
||||
foreach (var child in m_children) {
|
||||
child.DetachFromParent();
|
||||
}
|
||||
}
|
||||
m_children = value;
|
||||
UpdateChildren();
|
||||
}
|
||||
}
|
||||
QuadTreeNode m_parent;
|
||||
[JsonIgnore] public QuadTreeNode Parent => m_parent;
|
||||
void AttachToParent(QuadTreeNode parent, int index) {
|
||||
if (m_parent != null && m_parent != parent)
|
||||
throw new InvalidOperationException("Node already in a tree.");
|
||||
m_parent = parent;
|
||||
X = (parent.X << 1) | (index is 0 or 3 ? 1 : 0);
|
||||
Y = (parent.Y << 1) | (index is 0 or 1 ? 1 : 0);
|
||||
Z = parent.Z + 1;
|
||||
UpdateChildren();
|
||||
}
|
||||
void DetachFromParent() => m_parent = null;
|
||||
void UpdateChildren() {
|
||||
if (m_children != null) {
|
||||
for (int i = 0; i < m_children.Length; i++) {
|
||||
m_children[i].AttachToParent(this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
[JsonIgnore] public int X { get; private set; }
|
||||
[JsonIgnore] public int Y { get; private set; }
|
||||
[JsonIgnore] public int Z { get; private set; }
|
||||
public RegionData Data { get; set; }
|
||||
|
||||
public QuadTreeNode Get(Vector2 pos) {
|
||||
if ((pos.x is < 0 or >= 1) || (pos.y is < 0 or >= 1))
|
||||
return null;
|
||||
if (m_children == null)
|
||||
return this;
|
||||
Vector2 subPos = pos * 2;
|
||||
subPos.x %= 1;
|
||||
subPos.y %= 1;
|
||||
return pos.x >= 0.5f
|
||||
? (pos.y >= 0.5f ? m_children[0] : m_children[3]).Get(subPos)
|
||||
: (pos.y >= 0.5f ? m_children[1] : m_children[2]).Get(subPos);
|
||||
}
|
||||
|
||||
public void Merge() {
|
||||
Children = null;
|
||||
}
|
||||
|
||||
public void Split() {
|
||||
Children = new QuadTreeNode[] {
|
||||
new() { Data = Data.Copy() },
|
||||
new() { Data = Data.Copy() },
|
||||
new() { Data = Data.Copy() },
|
||||
new() { Data = Data.Copy() },
|
||||
};
|
||||
}
|
||||
}
|
||||
sealed record RegionData(string Id) {
|
||||
public string Id { get; set; } = Id;
|
||||
public RegionData Copy() => (RegionData)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Cryville.EEW.Unity/Map/RegionEditor.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/RegionEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd7b70d11ebfe324e830806e394cc334
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
68
Assets/Cryville.EEW.Unity/Map/RegionView.cs
Normal file
68
Assets/Cryville.EEW.Unity/Map/RegionView.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cryville.EEW.Unity.Map {
|
||||
sealed class RegionView : MonoBehaviour {
|
||||
[SerializeField]
|
||||
SpriteRenderer m_spriteRenderer;
|
||||
|
||||
Color _color;
|
||||
|
||||
bool m_isHovered;
|
||||
public bool IsHovered {
|
||||
get => m_isHovered;
|
||||
set {
|
||||
m_isHovered = value;
|
||||
UpdateColor();
|
||||
}
|
||||
}
|
||||
|
||||
bool m_isSelected;
|
||||
public bool IsSelected {
|
||||
get => m_isSelected;
|
||||
set {
|
||||
m_isSelected = value;
|
||||
UpdateColor();
|
||||
}
|
||||
}
|
||||
|
||||
bool m_isLeaf = true;
|
||||
public bool IsLeaf {
|
||||
get => m_isLeaf;
|
||||
set {
|
||||
m_isLeaf = value;
|
||||
UpdateColor();
|
||||
}
|
||||
}
|
||||
|
||||
string m_id;
|
||||
public string Id {
|
||||
get => m_id;
|
||||
set {
|
||||
m_id = value;
|
||||
unchecked {
|
||||
uint hash = (uint)value.GetHashCode();
|
||||
_color = Color.HSVToRGB(((hash >> 24) ^ ((hash >> 16) & 0xff) ^ ((hash >> 8) & 0xff) ^ (hash & 0xff)) / (float)0xff, 1, 1);
|
||||
}
|
||||
UpdateColor();
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(int x, int y, int z) {
|
||||
float scale = 1f / (1 << z);
|
||||
transform.localScale = new Vector3(scale, scale, 1);
|
||||
transform.localPosition = new Vector3(x * scale, y * scale - 1, -1 - z / 100f);
|
||||
}
|
||||
|
||||
void UpdateColor() {
|
||||
if (!m_isLeaf)
|
||||
_color.a = 0.0f;
|
||||
else if (m_isSelected)
|
||||
_color.a = 0.6f;
|
||||
else if (m_isHovered)
|
||||
_color.a = 0.4f;
|
||||
else
|
||||
_color.a = 0.2f;
|
||||
m_spriteRenderer.color = _color;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Cryville.EEW.Unity/Map/RegionView.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/RegionView.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c57a0e86eb63b6048ba265e9d98e84f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,4 +1,5 @@
|
||||
using Cryville.EEW.Colors;
|
||||
using Cryville.EEW.Core;
|
||||
using Cryville.EEW.Core.Colors;
|
||||
using Cryville.EEW.FERegion;
|
||||
using Cryville.EEW.Map;
|
||||
@@ -25,8 +26,23 @@ namespace Cryville.EEW.Unity {
|
||||
public IColorScheme ColorScheme { get; private set; } = new SeverityBasedColorScheme(DefaultSeverityScheme.Instance, DefaultSeverityColorMapping.Instance);
|
||||
public ISubColorScheme BorderColorScheme { get; private set; } = new WrappedColorScheme(new SeverityBasedColorScheme(DefaultSeverityScheme.Instance, DefaultSeverityColorMapping.SecondaryInstance));
|
||||
public ISubColorScheme TextColorScheme { get; private set; } = new DefaultTextColorScheme(Color.White, Color.Black);
|
||||
public ILocationConverter LocationConverter => new FERegionLongConverter(); // TODO TTS
|
||||
public TimeSpan NowcastWarningDelayTolerance => TimeSpan.FromMinutes(60); // TODO TTS
|
||||
public TimeSpan NowcastWarningDelayTolerance { get; private set; } = TimeSpan.FromMinutes(60);
|
||||
|
||||
public CultureInfo RVMCulture { get; private set; } = SharedCultures.CurrentUICulture;
|
||||
readonly int _infoLocationSpecificity = 3;
|
||||
readonly int _ttsLocationSpecificity = 3;
|
||||
readonly LocationNamer _locationNamer = new() { Namer = new FERegionLongNamer() };
|
||||
public bool NameLocation(double lat, double lon, CultureInfo localCulture, ref CultureInfo targetCulture, out string name, out int specificity) {
|
||||
specificity = _ttsLocationSpecificity;
|
||||
return _locationNamer.Name(lat, lon, localCulture, ref targetCulture, out name, ref specificity);
|
||||
}
|
||||
public bool NameLocation(double lat, double lon, CultureInfo localCulture, ref CultureInfo targetCulture, out string name) {
|
||||
int specificity = _infoLocationSpecificity;
|
||||
return _locationNamer.Name(lat, lon, localCulture, ref targetCulture, out name, ref specificity);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<TTSCultureConfig> TTSCultures { get; private set; }
|
||||
public bool DoIgnoreLanguageVariant { get; private set; }
|
||||
|
||||
public TimeZoneInfo OverrideTimeZone { get; private set; }
|
||||
public bool DoDisplayTimeZone { get; private set; } = true;
|
||||
@@ -101,9 +117,20 @@ namespace Cryville.EEW.Unity {
|
||||
"SREV" => new DefaultTextColorScheme(Color.White, Color.FromArgb(28, 28, 28), 0.555f),
|
||||
_ => new DefaultTextColorScheme(Color.White, Color.Black),
|
||||
};
|
||||
_locationNamer.Namer = config.LocationNamer switch {
|
||||
"FERegionShort" => new FERegionShortNamer(),
|
||||
_ => new FERegionLongNamer(),
|
||||
};
|
||||
if (config.NowcastWarningDelayTolerance is string nowcastWarningDelayTolerance)
|
||||
NowcastWarningDelayTolerance = TimeSpan.Parse(nowcastWarningDelayTolerance, CultureInfo.InvariantCulture);
|
||||
OverrideTimeZone = ParseTimeZone(config.OverrideTimeZone);
|
||||
DoDisplayTimeZone = config.DoDisplayTimeZone;
|
||||
DoSwitchBackToHistory = config.DoSwitchBackToHistory;
|
||||
RVMCulture = config.OverrideDisplayCulture is string rvmCulture
|
||||
? (string.IsNullOrEmpty(rvmCulture) ? SharedCultures.CurrentUICulture : SharedCultures.Get(rvmCulture))
|
||||
: CultureInfo.InvariantCulture;
|
||||
TTSCultures = config.TTSCultures ?? new List<TTSCultureConfig> { new(CultureInfo.InvariantCulture) };
|
||||
DoIgnoreLanguageVariant = config.DoIgnoreLanguageVariant;
|
||||
EventSources = config.EventSources;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cryville.EEW.Unity {
|
||||
class SoundPlayer : Core.SoundPlayer {
|
||||
class SoundPlayer : Core.Audio.SoundPlayer {
|
||||
public SoundPlayer() : base(GetEngineList(), AudioUsage.NotificationEvent) { }
|
||||
static List<Type> GetEngineList() => new() {
|
||||
typeof(Audio.Wasapi.MMDeviceEnumeratorWrapper),
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
using SpeechLib;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Cryville.EEW.Unity {
|
||||
class TTSWorker : Core.TTSWorker {
|
||||
public TTSWorker() : base(CreateSoundPlayer()) { }
|
||||
class TTSWorker : Core.Audio.TTSWorker {
|
||||
readonly ISpVoice _voice;
|
||||
|
||||
public TTSWorker() : base(CreateSoundPlayer()) {
|
||||
try {
|
||||
_voice = new SpVoiceClass();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
static SoundPlayer CreateSoundPlayer() {
|
||||
try {
|
||||
@@ -16,10 +24,25 @@ namespace Cryville.EEW.Unity {
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsSpeaking() => false;
|
||||
protected override bool IsSpeaking() {
|
||||
if (_voice == null) return false;
|
||||
_voice.GetStatus(out var status, out _);
|
||||
return (status.dwRunningState & (uint)SpeechRunState.SRSEIsSpeaking) != 0;
|
||||
}
|
||||
|
||||
protected override Task Speak(CultureInfo culture, string content, CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
protected override Task Speak(CultureInfo culture, string content, CancellationToken cancellationToken) {
|
||||
if (_voice == null) return Task.CompletedTask;
|
||||
_voice.Speak(
|
||||
string.Format(CultureInfo.InvariantCulture, "<LANG LANGID=\"{0:x}\">{1}</LANG>", culture.LCID, content),
|
||||
(uint)(SpeechVoiceSpeakFlags.SVSFlagsAsync | SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak),
|
||||
out _
|
||||
);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override void StopCurrent() { }
|
||||
protected override void StopCurrent() {
|
||||
if (_voice == null) return;
|
||||
_voice.Skip("SENTENCE", int.MaxValue, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Cryville.EEW.Unity.UI {
|
||||
child.SetViewModel(e);
|
||||
child.transform.SetParent(m_listView, false);
|
||||
_displayingViews.Add(child);
|
||||
OnDisplayingViewsChanged();
|
||||
|
||||
SwitchTo(_displayingReports.Count - 1);
|
||||
|
||||
@@ -37,6 +38,7 @@ namespace Cryville.EEW.Unity.UI {
|
||||
child.SetParent(null, false);
|
||||
Destroy(child.gameObject);
|
||||
_displayingViews.RemoveAt(index);
|
||||
OnDisplayingViewsChanged();
|
||||
|
||||
if (_displayingReports.Count == 0) {
|
||||
m_currentView.gameObject.SetActive(false);
|
||||
@@ -49,6 +51,14 @@ namespace Cryville.EEW.Unity.UI {
|
||||
|
||||
if (_displayingReports.Count <= 1) m_listView.gameObject.SetActive(false);
|
||||
}
|
||||
void OnDisplayingViewsChanged() {
|
||||
_maxBaseDuration = 1;
|
||||
foreach (var e in _displayingReports) {
|
||||
float duration = GetBaseDuration(e);
|
||||
if (duration > _maxBaseDuration)
|
||||
_maxBaseDuration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake() {
|
||||
if (Instance != null) {
|
||||
@@ -63,6 +73,7 @@ namespace Cryville.EEW.Unity.UI {
|
||||
|
||||
int _index = -1;
|
||||
float _tickDown;
|
||||
float _maxBaseDuration;
|
||||
void Update() {
|
||||
if (_displayingReports.Count == 0) return;
|
||||
_tickDown -= Time.deltaTime;
|
||||
@@ -77,12 +88,15 @@ namespace Cryville.EEW.Unity.UI {
|
||||
_index = index;
|
||||
var e = _displayingReports[index];
|
||||
m_currentView.SetViewModel(e, true);
|
||||
var keyProp = e.Properties.FirstOrDefault();
|
||||
_displayingViews[_index].SetCurrent(true);
|
||||
_tickDown = MathF.Exp(Math.Max(-1f, keyProp?.Severity ?? -1f) + 1);
|
||||
_tickDown = GetBaseDuration(e) / Math.Min(_maxBaseDuration, 4) * 4;
|
||||
m_currentView.gameObject.SetActive(true);
|
||||
Worker.Instance.SetCurrent(e);
|
||||
}
|
||||
static float GetBaseDuration(ReportViewModel e) {
|
||||
return MathF.Exp(Math.Max(-1f, e.Properties.FirstOrDefault()?.Severity ?? -1f) + 1);
|
||||
}
|
||||
|
||||
public void OnItemClicked(ReportViewModel viewModel) {
|
||||
int index = _displayingReports.IndexOf(viewModel);
|
||||
if (index == -1) return;
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace Cryville.EEW.Unity.UI {
|
||||
_textView = GetComponent<TMP_Text>();
|
||||
}
|
||||
|
||||
StringBuilder _sb = new();
|
||||
char[] _buffer = new char[256];
|
||||
readonly StringBuilder _sb = new();
|
||||
readonly char[] _buffer = new char[256];
|
||||
void Update() {
|
||||
_sb.Clear();
|
||||
_sb.AppendFormat(
|
||||
|
||||
@@ -64,6 +64,9 @@ namespace Cryville.EEW.Unity {
|
||||
BuildWorkers();
|
||||
_worker.RVMGeneratorContext = SharedSettings.Instance;
|
||||
_worker.TTSMessageGeneratorContext = SharedSettings.Instance;
|
||||
_worker.RVMCulture = SharedSettings.Instance.RVMCulture;
|
||||
_worker.SetTTSCultures(SharedSettings.Instance.TTSCultures ?? new TTSCultureConfig[0]);
|
||||
_worker.IgnoreLanguageVariant = SharedSettings.Instance.DoIgnoreLanguageVariant;
|
||||
_ongoingReportManager.Changed += OnOngoingReported;
|
||||
_worker.Reported += OnReported;
|
||||
_grouper.GroupUpdated += OnGroupUpdated;
|
||||
@@ -87,9 +90,10 @@ namespace Cryville.EEW.Unity {
|
||||
_ongoingReportManager.Dispose();
|
||||
}
|
||||
|
||||
static void RegisterViewModelGenerators(CoreWorker worker) {
|
||||
CENCEarthquakeRVMGenerator _cencEarthquakeRVMGenerator;
|
||||
void RegisterViewModelGenerators(CoreWorker worker) {
|
||||
worker.RegisterViewModelGenerator(new BMKGEarthquakeRVMGenerator());
|
||||
worker.RegisterViewModelGenerator(new CENCEarthquakeRVMGenerator());
|
||||
worker.RegisterViewModelGenerator(_cencEarthquakeRVMGenerator = new CENCEarthquakeRVMGenerator());
|
||||
worker.RegisterViewModelGenerator(new CENCEEWRVMGenerator());
|
||||
worker.RegisterViewModelGenerator(new CWAEarthquakeRVMGenerator());
|
||||
worker.RegisterViewModelGenerator(new CWAEEWRVMGenerator());
|
||||
@@ -104,9 +108,10 @@ namespace Cryville.EEW.Unity {
|
||||
worker.RegisterViewModelGenerator(new SichuanEEWRVMGenerator());
|
||||
worker.RegisterViewModelGenerator(new VersionRVMGenerator());
|
||||
}
|
||||
static void RegisterTTSMessageGenerators(CoreWorker worker) {
|
||||
CENCEarthquakeTTSMessageGenerator _cencEarthquakeTTSMessageGenerator;
|
||||
void RegisterTTSMessageGenerators(CoreWorker worker) {
|
||||
worker.RegisterTTSMessageGenerator(new BMKGEarthquakeTTSMessageGenerator());
|
||||
worker.RegisterTTSMessageGenerator(new CENCEarthquakeTTSMessageGenerator());
|
||||
worker.RegisterTTSMessageGenerator(_cencEarthquakeTTSMessageGenerator = new CENCEarthquakeTTSMessageGenerator());
|
||||
worker.RegisterTTSMessageGenerator(new CENCEEWTTSMessageGenerator());
|
||||
worker.RegisterTTSMessageGenerator(new CWAEarthquakeTTSMessageGenerator());
|
||||
worker.RegisterTTSMessageGenerator(new CWAEEWTTSMessageGenerator());
|
||||
@@ -129,7 +134,7 @@ namespace Cryville.EEW.Unity {
|
||||
BMKGOpenDataWorker bmkgWorker = new(new Uri("http://localhost:9095/autogempa.json"));
|
||||
bmkgWorker.SetDataUris(new Uri[] { new("http://localhost:9095/gempadirasakan.json") });
|
||||
_worker.AddWorker(bmkgWorker);
|
||||
_worker.AddWorker(new NOAAAtomWorker(new("http://localhost:9095/PAAQAtom.xml")));
|
||||
_worker.AddWorker(new NOAAAtomWorker(new("http://localhost:9095/PAAQAtom.xml"), forceHttps: false));
|
||||
_worker.AddWorker(new UpdateCheckerWorker(typeof(Worker).Assembly.GetName().Version?.ToString(3) ?? "", "unity"));
|
||||
#else
|
||||
foreach (var source in SharedSettings.Instance.EventSources) {
|
||||
@@ -163,7 +168,7 @@ namespace Cryville.EEW.Unity {
|
||||
worker.IsFilterWhitelist = config.IsFilterWhitelist;
|
||||
return worker;
|
||||
}
|
||||
static WolfxWorker BuildWolfxWorkerFilter(WolfxWorker worker, WolfxEventSourceConfig config) {
|
||||
WolfxWorker BuildWolfxWorkerFilter(WolfxWorker worker, WolfxEventSourceConfig config) {
|
||||
if (config.Filter != null) worker.SetFilter(config.Filter.Select(i => i switch {
|
||||
"cenc_eew" => typeof(CENCEEW),
|
||||
"cenc_eqlist" => typeof(WolfxEarthquakeList<CENCEarthquake>),
|
||||
@@ -174,6 +179,11 @@ namespace Cryville.EEW.Unity {
|
||||
_ => throw new InvalidOperationException("Unknown Wolfx event type."),
|
||||
}));
|
||||
worker.IsFilterWhitelist = config.IsFilterWhitelist;
|
||||
|
||||
_cencEarthquakeRVMGenerator.UseRawLocationName
|
||||
= _cencEarthquakeTTSMessageGenerator.UseRawLocationName
|
||||
= config.UseRawCENCLocationName;
|
||||
|
||||
return worker;
|
||||
}
|
||||
static BMKGOpenDataWorker BuildBMKGOpenDataWorkerUris(BMKGOpenDataWorker worker, BMKGOpenDataEventSourceConfig config) {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Assets/Plugins/Cryville.EEW.CENCStrongGroundMotion.Map.dll
Normal file
BIN
Assets/Plugins/Cryville.EEW.CENCStrongGroundMotion.Map.dll
Normal file
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 550171b48a648b34e9ce5f1aba6244f1
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Plugins/Cryville.EEW.CENCStrongGroundMotion.dll
Normal file
BIN
Assets/Plugins/Cryville.EEW.CENCStrongGroundMotion.dll
Normal file
Binary file not shown.
33
Assets/Plugins/Cryville.EEW.CENCStrongGroundMotion.dll.meta
Normal file
33
Assets/Plugins/Cryville.EEW.CENCStrongGroundMotion.dll.meta
Normal file
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f848a4ea2f35e7449e584beee48c659
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user