/// <summary>
        /// Merges all provinces in each country so their number fits a given range
        /// </summary>
        /// <param name="min">Minimum number of provinces.</param>
        /// <param name="max">Maximum number of provinces.</param>
        public void ProvincesEqualize(int min, int max)
        {
            if (min < 1)
            {
                return;
            }
            if (max < min)
            {
                max = min;
            }

            map.showProvinces    = true;
            map.drawAllProvinces = true;

            for (int c = 0; c < map.countries.Length; c++)
            {
                Country country = map.countries [c];
                //				if (country.name.Equals("Spain")) {
                //					Debug.Log ("Spain!");
                //				}
                if (country == null || country.provinces == null)
                {
                    continue;
                }
                int   targetProvCount = UnityEngine.Random.Range(min, max);
                int   provCount       = country.provinces.Length;
                float provStartSize   = 0;
                while (provCount > targetProvCount)
                {
                    // Take the smaller province and merges with a neighbour
                    float minAreaSize   = float.MaxValue;
                    int   provinceIndex = -1;
                    for (int p = 0; p < provCount; p++)
                    {
                        Province prov = country.provinces [p];
                        if (prov == null)
                        {
                            continue;
                        }
                        if (prov.regions == null)
                        {
                            map.ReadProvincePackedString(prov);
                        }
                        if (prov.regions == null || prov.regions.Count == 0 || prov.mainRegion.neighbours == null || prov.mainRegion.neighbours.Count == 0)
                        {
                            continue;
                        }
                        if (prov.regionsRect2DArea < minAreaSize && prov.regionsRect2DArea > provStartSize)
                        {
                            minAreaSize   = prov.regionsRect2DArea;
                            provinceIndex = map.GetProvinceIndex(prov);
                        }
                    }

                    if (provinceIndex < 0)
                    {
                        break;
                    }

                    provStartSize = minAreaSize;

                    // Get the smaller neighbour
                    int      neighbourIndex = -1;
                    Province province       = map.provinces [provinceIndex];
                    int      neighbourCount = province.mainRegion.neighbours.Count;
                    minAreaSize = float.MaxValue;
                    for (int n = 0; n < neighbourCount; n++)
                    {
                        Region   neighbour         = province.mainRegion.neighbours [n];
                        Province neighbourProvince = (Province)neighbour.entity;
                        if (neighbourProvince != null && neighbourProvince != province && neighbourProvince.countryIndex == c && neighbour.rect2DArea < minAreaSize)
                        {
                            int neighbourProvIndex = map.GetProvinceIndex(neighbourProvince);
                            if (neighbourProvIndex >= 0)
                            {
                                minAreaSize    = neighbour.rect2DArea;
                                neighbourIndex = neighbourProvIndex;
                            }
                        }
                    }
                    if (neighbourIndex < 0)
                    {
                        continue;
                    }

                    // Merges province into neighbour
                    string provinceSource = map.provinces [provinceIndex].name;
                    string provinceTarget = map.provinces [neighbourIndex].name;
                    if (!map.ProvinceTransferProvinceRegion(neighbourIndex, map.provinces [provinceIndex].mainRegion, false))
                    {
                        Debug.LogWarning("Country: " + map.countries [c].name + " => " + provinceSource + " failed merge into " + provinceTarget + ".");
                        break;
                    }
                    provCount = country.provinces.Length;
                }
            }
            map.Redraw();
            provinceChanges   = true;
            cityChanges       = true;
            mountPointChanges = true;
        }
        /// <summary>
        /// Makes provinceIndex absorb another province providing any of its regions. All regions are transfered to target province.
        /// This function is quite slow with high definition frontiers.
        /// </summary>
        /// <param name="provinceIndex">Province index of the conquering province.</param>
        /// <param name="sourceRegion">Source region of the loosing province.</param>
        public bool ProvinceTransferProvinceRegion(int provinceIndex, Region sourceProvinceRegion, bool redraw)
        {
            int sourceProvinceIndex = GetProvinceIndex((Province)sourceProvinceRegion.entity);

            if (provinceIndex < 0 || sourceProvinceIndex < 0 || provinceIndex == sourceProvinceIndex)
            {
                return(false);
            }

            // Transfer cities
            Province sourceProvince = provinces[sourceProvinceIndex];
            Province targetProvince = provinces[provinceIndex];

            if (sourceProvince.countryIndex != targetProvince.countryIndex)
            {
                // Transfer source province to target country province
                if (!CountryTransferProvinceRegion(targetProvince.countryIndex, sourceProvinceRegion))
                {
                    return(false);
                }
            }

            int cityCount = cities.Count;

            for (int k = 0; k < cityCount; k++)
            {
                if (_cities[k].countryIndex == sourceProvince.countryIndex && _cities[k].province.Equals(sourceProvince.name))
                {
                    _cities[k].province = targetProvince.name;
                }
            }

            // Transfer mount points
            int mountPointCount = mountPoints.Count;

            for (int k = 0; k < mountPointCount; k++)
            {
                if (mountPoints[k].provinceIndex == sourceProvinceIndex)
                {
                    mountPoints[k].provinceIndex = provinceIndex;
                }
            }

            // Transfer regions
            if (sourceProvince.regions.Count > 0)
            {
                List <Region> targetRegions = new List <Region>(targetProvince.regions);
                for (int k = 0; k < sourceProvince.regions.Count; k++)
                {
                    targetRegions.Add(sourceProvince.regions[k]);
                }
                targetProvince.regions = targetRegions;
            }

            // Fusion any adjacent regions that results from merge operation
            ProvinceMergeAdjacentRegions(targetProvince);
            RegionSanitize(targetProvince.regions, false);
            targetProvince.mainRegionIndex = 0;             // will be updated on RefreshProvinceDefinition

            // Finish operation
            ProvinceDeleteAndDependencies(sourceProvinceIndex);
            if (provinceIndex > sourceProvinceIndex)
            {
                provinceIndex--;
            }
            if (redraw)
            {
                RefreshProvinceDefinition(provinceIndex);
            }
            else
            {
                RefreshProvinceGeometry(provinceIndex);
            }
//			ResortCountryProvinces(provinces[provinceIndex].countryIndex);
            return(true);
        }
Beispiel #3
0
        void CreateProvincesMesh(int countryIndex, bool includeNeighbours)
        {
            // prepare a list with the countries to be drawn
            countryProvincesDrawnIndex = countryIndex;
            List <Country> targetCountries = new List <Country> (20);

            // add selected country
            targetCountries.Add(countries [countryIndex]);
            // add neighbour countries?
            if (includeNeighbours)
            {
                for (int k = 0; k < countries[countryIndex].regions.Count; k++)
                {
                    List <Region> neighbours = countries [countryIndex].regions [k].neighbours;
                    for (int n = 0; n < neighbours.Count; n++)
                    {
                        Country c = (Country)neighbours [n].entity;
                        if (!targetCountries.Contains(c))
                        {
                            targetCountries.Add(c);
                        }
                    }
                }
            }

            if (provinceFrontiersPoints == null)
            {
                provinceFrontiersPoints = new List <Vector3> (200000);
            }
            else
            {
                provinceFrontiersPoints.Clear();
            }
            if (provinceFrontiersCacheHit == null)
            {
                provinceFrontiersCacheHit = new Dictionary <Vector3, Region> (200000);
            }
            else
            {
                provinceFrontiersCacheHit.Clear();
            }

            for (int c = 0; c < targetCountries.Count; c++)
            {
                Country targetCountry = targetCountries[c];
                if (targetCountry.provinces == null)
                {
                    continue;
                }
                for (int p = 0; p < targetCountry.provinces.Length; p++)
                {
                    Province province = targetCountry.provinces [p];
                    if (province.regions == null)                       // read province data the first time we need it
                    {
                        ReadProvincePackedString(province);
                    }
                    for (int r = 0; r < province.regions.Count; r++)
                    {
                        Region region = province.regions [r];
                        region.entity      = province;
                        region.regionIndex = r;
                        region.neighbours.Clear();
                        int max = region.points.Length - 1;
                        for (int i = 0; i < max; i++)
                        {
                            Vector3 p0 = region.points [i];
                            Vector3 p1 = region.points [i + 1];
                            Vector3 hc = p0 + p1;
                            if (provinceFrontiersCacheHit.ContainsKey(hc))
                            {
                                Region neighbour = provinceFrontiersCacheHit [hc];
                                if (neighbour != region)
                                {
                                    if (!region.neighbours.Contains(neighbour))
                                    {
                                        region.neighbours.Add(neighbour);
                                        neighbour.neighbours.Add(region);
                                    }
                                }
                            }
                            else
                            {
                                provinceFrontiersCacheHit.Add(hc, region);
                                provinceFrontiersPoints.Add(p0);
                                provinceFrontiersPoints.Add(p1);
                            }
                        }
                        // Close the polygon
                        provinceFrontiersPoints.Add(region.points [max]);
                        provinceFrontiersPoints.Add(region.points [0]);
                    }
                }
            }

            int meshGroups = (provinceFrontiersPoints.Count / 65000) + 1;
            int meshIndex  = -1;

            provinceFrontiersIndices = new int[meshGroups][];
            provinceFrontiers        = new Vector3[meshGroups][];
            for (int k = 0; k < provinceFrontiersPoints.Count; k += 65000)
            {
                int max = Mathf.Min(provinceFrontiersPoints.Count - k, 65000);
                provinceFrontiers [++meshIndex]      = new Vector3[max];
                provinceFrontiersIndices [meshIndex] = new int[max];
                for (int j = k; j < k + max; j++)
                {
                    provinceFrontiers [meshIndex] [j - k]        = provinceFrontiersPoints [j];
                    provinceFrontiersIndices [meshIndex] [j - k] = j - k;
                }
            }

            // Create province borders container
            GameObject provinceObj = new GameObject(countryIndex.ToString());

            provinceObj.transform.SetParent(provincesLayer.transform, false);
            provinceObj.transform.localPosition = MiscVector.Vector3zero;
            provinceObj.transform.localRotation = Quaternion.Euler(MiscVector.Vector3zero);

            for (int k = 0; k < provinceFrontiers.Length; k++)
            {
                GameObject flayer = new GameObject("flayer");
                flayer.transform.SetParent(provinceObj.transform, false);
                flayer.transform.localPosition = MiscVector.Vector3zero;
                flayer.transform.localRotation = Quaternion.Euler(MiscVector.Vector3zero);

                Mesh mesh = new Mesh();
                mesh.vertices = provinceFrontiers [k];
                mesh.SetIndices(provinceFrontiersIndices [k], MeshTopology.Lines, 0);
                mesh.RecalculateBounds();
                mesh.hideFlags = HideFlags.DontSave;

                MeshFilter mf = flayer.AddComponent <MeshFilter> ();
                mf.sharedMesh = mesh;

                MeshRenderer mr = flayer.AddComponent <MeshRenderer> ();
                mr.receiveShadows       = false;
                mr.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
                mr.shadowCastingMode    = UnityEngine.Rendering.ShadowCastingMode.Off;
                mr.useLightProbes       = false;
                mr.sharedMaterial       = provincesMat;
            }

            if (_showOutline)
            {
                Country country = countries[countryIndex];
                Region  region  = country.regions[country.mainRegionIndex];
                provinceCountryOutlineRef = DrawCountryRegionOutline(region, provinceObj);
            }
        }
Beispiel #4
0
        bool GetProvinceUnderMouse(int countryIndex, Vector3 spherePoint, out int provinceIndex, out int regionIndex)
        {
            float startingDistance = 0;

            provinceIndex = regionIndex = -1;
            Country country = countries[countryIndex];

            if (country.provinces == null)
            {
                return(false);
            }
            int provincesCount = country.provinces.Length;

            if (provincesCount == 0)
            {
                return(false);
            }

            PolygonPoint latlonPos = GetLatLonFromSpherePoint(spherePoint);
            float        maxArea   = float.MaxValue;

            // Is this the same province currently selected?
            if (_provinceHighlightedIndex >= 0 && _provinceRegionHighlightedIndex >= 0 && provinces[_provinceHighlightedIndex].countryIndex == countryIndex)
            {
                Region region = provinces[_provinceHighlightedIndex].regions[_provinceRegionHighlightedIndex];
//				if (ContainsPoint2D (region.latlon, latlonPos.X, latlonPos.Y)) {
                if (region.ContainsPoint(latlonPos.X, latlonPos.Y))
                {
                    maxArea = region.entity.mainRegionArea;
                    // cannot return yet - need to check if any other province (smaller than this) could be highlighted
//					provinceIndex = _provinceHighlightedIndex;
//					regionIndex = _provinceRegionHighlightedIndex;
//					return true;
                }
            }

            // Check other provinces
            for (int tries = 0; tries < 75; tries++)
            {
                float minDist = float.MaxValue;
                for (int p = 0; p < provincesCount; p++)
                {
                    Province province = country.provinces[p];
                    if (province.regions == null || province.mainRegionArea > maxArea)
                    {
                        continue;
                    }
                    for (int pr = 0; pr < province.regions.Count; pr++)
                    {
                        Vector3 regionCenter = province.regions [pr].center;
                        float   dist         = (regionCenter - spherePoint).sqrMagnitude;
                        if (dist > startingDistance && dist < minDist)
                        {
                            minDist       = dist;
                            provinceIndex = GetProvinceIndex(province);
                            regionIndex   = pr;
                        }
                    }
                }

                // Check if this region is visible and the mouse is inside
                if (provinceIndex >= 0)
                {
                    Region region = provinces[provinceIndex].regions[regionIndex];
                    if (region.ContainsPoint(latlonPos.X, latlonPos.Y))
                    {
                        return(true);
                    }
                }

                // Continue searching but farther centers
                startingDistance = minDist;
            }
            return(false);
        }
Beispiel #5
0
        public void ReadProvincePackedString(Province province)
        {
            string[] regions     = province.packedRegions.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
            int      regionCount = regions.Length;
            float    maxArea     = 0;

            province.regions = new List <Region> (regionCount);
            List <Vector3>      regionPoints = new List <Vector3>(10000);
            List <PolygonPoint> latlons      = new List <PolygonPoint>(10000);

            for (int r = 0; r < regionCount; r++)
            {
                string[] coordinates    = regions [r].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                int      coorCount      = coordinates.Length;
                Vector2  minMaxLat      = new Vector2(float.MaxValue, float.MinValue);
                Vector4  minMaxLon      = new Vector2(float.MaxValue, float.MinValue);
                Region   provinceRegion = new Region(province, province.regions.Count);
                provinceRegion.points = new Vector3[coorCount];
                provinceRegion.latlon = new PolygonPoint[coorCount];
                regionPoints.Clear();
                latlons.Clear();
                int regionPointsIndex = -1;
                for (int c = 0; c < coorCount; c++)
                {
                    float lat, lon;
                    GetLatLonFromPackedString(coordinates[c], out lat, out lon);

                    // Convert to sphere coordinates
                    Vector3 point = GetSpherePointFromLatLon(lat, lon);
                    if (regionPointsIndex > -1 && regionPoints[regionPointsIndex] == point)
                    {
                        continue;
                    }

                    regionPointsIndex++;
                    regionPoints.Add(point);
                    latlons.Add(new PolygonPoint(lat, lon));

                    if (lat < minMaxLat.x)
                    {
                        minMaxLat.x = lat;
                    }
                    if (lat > minMaxLat.y)
                    {
                        minMaxLat.y = lat;
                    }
                    if (lon < minMaxLon.x)
                    {
                        minMaxLon.x = lon;
                    }
                    if (lon > minMaxLon.y)
                    {
                        minMaxLon.y = lon;
                    }
                }
                provinceRegion.points    = regionPoints.ToArray();
                provinceRegion.latlon    = latlons.ToArray();
                provinceRegion.minMaxLat = minMaxLat;
                provinceRegion.minMaxLon = minMaxLon;
                provinceRegion.rect2D    = GetRect2DFromMinMaxLatLon(minMaxLat, minMaxLon);
                Vector2 midLatLon        = new Vector2((minMaxLat.x + minMaxLat.y) / 2, (minMaxLon.x + minMaxLon.y) / 2);
                Vector3 normRegionCenter = GetSpherePointFromLatLon(midLatLon.x, midLatLon.y);
                provinceRegion.center = normRegionCenter;

                float area = provinceRegion.rect2D.size.sqrMagnitude;
                if (area > maxArea)
                {
                    maxArea = area;
                    province.mainRegionIndex = r;
                    province.mainRegionArea  = provinceRegion.rect2D.width * provinceRegion.rect2D.height;
                    province.center          = provinceRegion.center;
                }
                province.regions.Add(provinceRegion);
            }
//			Debug.Log ("Total read: " + (DateTime.Now - start).TotalMilliseconds);
        }
Beispiel #6
0
        /// <summary>
        /// Used internally by the Map Editor. It will recalculate de boundaries and optimize frontiers based on new data of provinces array
        /// </summary>
        public void RefreshProvinceDefinition(int provinceIndex)
        {
            lastProvinceLookupCount = -1;
            if (provinceIndex < 0 || provinceIndex >= provinces.Length)
            {
                return;
            }
            float    maxArea  = 0;
            Province province = provinces [provinceIndex];

            if (province.regions == null)
            {
                ReadProvincePackedString(province);                                     // safe check for specific map editor situations
            }
            int regionCount = province.regions.Count;

            for (int r = 0; r < regionCount; r++)
            {
                Vector2 minMaxLat      = new Vector2(float.MaxValue, float.MinValue);
                Vector4 minMaxLon      = new Vector2(float.MaxValue, float.MinValue);
                Region  provinceRegion = province.regions[r];
                provinceRegion.entity      = province;
                provinceRegion.regionIndex = r;
                int coorCount = provinceRegion.latlon.Length;
                for (int c = 0; c < coorCount; c++)
                {
                    PolygonPoint latlon = provinceRegion.latlon[c];
                    latlon.Reset();
                    float lat = latlon.Xf;
                    float lon = latlon.Yf;
                    if (lat < minMaxLat.x)
                    {
                        minMaxLat.x = lat;
                    }
                    if (lat > minMaxLat.y)
                    {
                        minMaxLat.y = lat;
                    }
                    if (lon < minMaxLon.x)
                    {
                        minMaxLon.x = lon;
                    }
                    if (lon > minMaxLon.y)
                    {
                        minMaxLon.y = lon;
                    }
                }
                provinceRegion.minMaxLat = minMaxLat;
                provinceRegion.minMaxLon = minMaxLon;
                provinceRegion.rect2D    = GetRect2DFromMinMaxLatLon(minMaxLat, minMaxLon);
                Vector2 midLatLon        = new Vector2((minMaxLat.x + minMaxLat.y) / 2, (minMaxLon.x + minMaxLon.y) / 2);
                Vector3 normRegionCenter = GetSpherePointFromLatLon(midLatLon.x, midLatLon.y);
                provinceRegion.center = normRegionCenter;

                float area = provinceRegion.rect2D.size.sqrMagnitude;
                if (area > maxArea)
                {
                    maxArea = area;
                    province.mainRegionIndex = r;
                    province.center          = provinceRegion.center;
                }
            }
            DrawProvinces(provinces [provinceIndex].countryIndex, true, true);
        }
        GameObject GenerateProvinceRegionSurface(int provinceIndex, int regionIndex, Material material)
        {
            if (provinceIndex < 0 || provinceIndex >= provinces.Length)
            {
                return(null);
            }
            if (provinces[provinceIndex].regions == null)
            {
                ReadProvincePackedString(provinces[provinceIndex]);
            }
            if (provinces[provinceIndex].regions == null || regionIndex < 0 || regionIndex >= provinces[provinceIndex].regions.Count)
            {
                return(null);
            }

            Province province = provinces[provinceIndex];
            Region   region   = province.regions [regionIndex];

            // Triangulate to get the polygon vertex indices
            Poly2Tri.Polygon poly = new Poly2Tri.Polygon(region.latlon);

            if (_enableProvinceEnclaves && regionIndex == province.mainRegionIndex)
            {
                ProvinceSubstractProvinceEnclaves(provinceIndex, region, poly);
            }


            // Antarctica, Saskatchewan (Canada), British Columbia (Canada), Krasnoyarsk (Russia) - special cases due to its geometry
            float step = _frontiersDetail == FRONTIERS_DETAIL.High ? 2f: 5f;
            List <TriangulationPoint> steinerPoints = new List <TriangulationPoint>();
            float x0 = region.latlonRect2D.min.x + step / 2f;
            float x1 = region.latlonRect2D.max.x - step / 2f;
            float y0 = region.latlonRect2D.min.y + step / 2f;
            float y1 = region.latlonRect2D.max.y - step / 2f;

            for (float x = x0; x < x1; x += step)
            {
                for (float y = y0; y < y1; y += step)
                {
                    float xp = x + UnityEngine.Random.Range(-0.0001f, 0.0001f);
                    float yp = y + UnityEngine.Random.Range(-0.0001f, 0.0001f);
                    if (region.Contains(xp, yp))
                    {
                        steinerPoints.Add(new TriangulationPoint(xp, yp));
                        //						GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                        //						obj.transform.SetParent(WorldMapGlobe.instance.transform, false);
                        //						obj.transform.localScale = Vector3.one * 0.01f;
                        //						obj.transform.localPosition = Conversion.GetSpherePointFromLatLon(new Vector2(x,y)) * 1.01f;
                    }
                }
            }
            if (steinerPoints.Count > 0)
            {
                poly.AddSteinerPoints(steinerPoints);
            }
            P2T.Triangulate(poly);

            int flip1, flip2;

            if (_earthInvertedMode)
            {
                flip1 = 2; flip2 = 1;
            }
            else
            {
                flip1 = 1; flip2 = 2;
            }
            int triCount = poly.Triangles.Count;

            Vector3[] revisedSurfPoints = new Vector3[triCount * 3];
            for (int k = 0; k < triCount; k++)
            {
                DelaunayTriangle dt = poly.Triangles[k];
                revisedSurfPoints[k * 3]         = Conversion.GetSpherePointFromLatLon(dt.Points[0].X, dt.Points[0].Y);
                revisedSurfPoints[k * 3 + flip1] = Conversion.GetSpherePointFromLatLon(dt.Points[1].X, dt.Points[1].Y);
                revisedSurfPoints[k * 3 + flip2] = Conversion.GetSpherePointFromLatLon(dt.Points[2].X, dt.Points[2].Y);
            }

            int revIndex = revisedSurfPoints.Length - 1;

            // Generate surface mesh
            int    cacheIndex    = GetCacheIndexForProvinceRegion(provinceIndex, regionIndex);
            string cacheIndexSTR = cacheIndex.ToString();
            // Deletes potential residual surface
            Transform t = surfacesLayer.transform.Find(cacheIndexSTR);

            if (t != null)
            {
                DestroyImmediate(t.gameObject);
            }
            GameObject surf = Drawing.CreateSurface(cacheIndexSTR, revisedSurfPoints, revIndex, material);

//			Rect rect = new Rect(0,0,0,0);
//			GameObject surf = Drawing.CreateSurface (cacheIndexSTR, poly, material, rect, Vector2.zero, Vector2.zero, 0);
            surf.transform.SetParent(surfacesLayer.transform, false);
            surf.transform.localPosition = Misc.Vector3zero;
            if (_earthInvertedMode)
            {
                surf.transform.localScale = Misc.Vector3one * 0.998f;
            }
            if (surfaces.ContainsKey(cacheIndex))
            {
                surfaces.Remove(cacheIndex);
            }
            surfaces.Add(cacheIndex, surf);
            return(surf);
        }
        /// <summary>
        /// Calculates correct province for cities
        /// </summary>
        public void FixOrphanCities()
        {
            if (_map.provinces == null)
            {
                return;
            }

            int countryAssigned  = 0;
            int provinceAssigned = 0;

            int cityCount = _map.cities.Count;

            for (int c = 0; c < cityCount; c++)
            {
                City city = _map.cities[c];
                if (city.countryIndex == -1)
                {
                    for (int k = 0; k < _map.countries.Length; k++)
                    {
                        Country co = _map.countries[k];
                        if (co.regions == null)
                        {
                            continue;
                        }
                        int regCount = co.regions.Count;
                        for (int kr = 0; kr < regCount; kr++)
                        {
                            if (co.regions[kr].Contains(city.latlon))
                            {
                                city.countryIndex = k;
                                countryAssigned++;
                                k = 100000;
                                break;
                            }
                        }
                    }
                }
                if (city.countryIndex == -1)
                {
                    float minDist = float.MaxValue;
                    for (int k = 0; k < _map.countries.Length; k++)
                    {
                        Country co = _map.countries[k];
                        if (co.regions == null)
                        {
                            continue;
                        }
                        int regCount = co.regions.Count;
                        for (int kr = 0; kr < regCount; kr++)
                        {
                            float dist = (co.regions[kr].latlonCenter - city.latlon).sqrMagnitude;
                            if (dist < minDist)
                            {
                                minDist           = dist;
                                city.countryIndex = k;
                                countryAssigned++;
                            }
                        }
                    }
                }

                if (city.province.Length == 0)
                {
                    Country country = _map.countries[city.countryIndex];
                    if (country.provinces == null)
                    {
                        continue;
                    }
                    for (int p = 0; p < country.provinces.Length; p++)
                    {
                        Province province = country.provinces[p];
                        if (province.regions == null)
                        {
                            _map.ReadProvincePackedString(province);
                        }
                        if (province.regions == null)
                        {
                            continue;
                        }
                        int regCount = province.regions.Count;
                        for (int pr = 0; pr < regCount; pr++)
                        {
                            Region reg = province.regions[pr];
                            if (reg.Contains(city.latlon))
                            {
                                city.province = province.name;
                                p             = 100000;
                                break;
                            }
                        }
                    }
                }
            }

            for (int c = 0; c < cityCount; c++)
            {
                City city = _map.cities[c];
                if (city.province.Length == 0)
                {
                    float minDist = float.MaxValue;
                    int   pg      = -1;
                    for (int p = 0; p < _map.provinces.Length; p++)
                    {
                        Province province = _map.provinces[p];
                        if (province.regions == null)
                        {
                            _map.ReadProvincePackedString(province);
                        }
                        if (province.regions == null)
                        {
                            continue;
                        }
                        int regCount = province.regions.Count;
                        for (int pr = 0; pr < regCount; pr++)
                        {
                            Region pregion = province.regions[pr];
                            for (int prp = 0; prp < pregion.latlon.Length; prp++)
                            {
                                float dist = (city.latlon - pregion.latlon[prp]).sqrMagnitude;
                                if (dist < minDist)
                                {
                                    minDist = dist;
                                    pg      = p;
                                }
                            }
                        }
                    }
                    if (pg >= 0)
                    {
                        city.province = _map.provinces[pg].name;
                        provinceAssigned++;
                    }
                }
            }

            Debug.Log(countryAssigned + " cities were assigned a new country.");
            Debug.Log(provinceAssigned + " cities were assigned a new province.");

            if (countryAssigned > 0 || provinceAssigned > 0)
            {
                cityChanges = true;
            }
        }
        bool DrawProvinces(List <Country> targetCountries, bool forceRefresh)
        {
            if (provinceFrontiersPoints == null)
            {
                provinceFrontiersPoints = new List <Vector3> (200000);
            }
            else
            {
                provinceFrontiersPoints.Clear();
            }
            if (provinceFrontiersCacheHit == null)
            {
                provinceFrontiersCacheHit = new Dictionary <Vector3, Region> (200000);
            }
            else
            {
                provinceFrontiersCacheHit.Clear();
            }

            int countriesCount = targetCountries.Count;

            for (int c = 0; c < countriesCount; c++)
            {
                Country targetCountry = targetCountries[c];
                if (targetCountry.provinces == null)
                {
                    continue;
                }
                for (int p = 0; p < targetCountry.provinces.Length; p++)
                {
                    Province province = targetCountry.provinces [p];
                    int      regCount = province.regions.Count;
                    for (int r = 0; r < regCount; r++)
                    {
                        Region region = province.regions [r];
                        region.entity      = province;
                        region.regionIndex = r;
                        region.neighbours.Clear();
                        int max = region.latlon.Length - 1;
                        for (int i = 0; i < max; i++)
                        {
                            Vector3 p0 = region.spherePoints [i];
                            Vector3 p1 = region.spherePoints [i + 1];
                            Vector3 hc = p0 + p1;
                            if (provinceFrontiersCacheHit.ContainsKey(hc))
                            {
                                Region neighbour = provinceFrontiersCacheHit [hc];
                                if (neighbour != region)
                                {
                                    if (!region.neighbours.Contains(neighbour))
                                    {
                                        region.neighbours.Add(neighbour);
                                        neighbour.neighbours.Add(region);
                                    }
                                }
                            }
                            else
                            {
                                provinceFrontiersCacheHit.Add(hc, region);
                                provinceFrontiersPoints.Add(p0);
                                provinceFrontiersPoints.Add(p1);
                            }
                        }
                        // Close the polygon
                        provinceFrontiersPoints.Add(region.spherePoints [max]);
                        provinceFrontiersPoints.Add(region.spherePoints [0]);
                    }
                }
            }

            int meshGroups = (provinceFrontiersPoints.Count / 65000) + 1;
            int meshIndex  = -1;

            provinceFrontiersIndices = new int[meshGroups][];
            provinceFrontiers        = new Vector3[meshGroups][];
            int pcount = provinceFrontiersPoints.Count;

            for (int k = 0; k < pcount; k += 65000)
            {
                int max = Mathf.Min(provinceFrontiersPoints.Count - k, 65000);
                provinceFrontiers [++meshIndex]      = new Vector3[max];
                provinceFrontiersIndices [meshIndex] = new int[max];
                int jend = k + max;
                for (int j = k; j < jend; j++)
                {
                    provinceFrontiers [meshIndex] [j - k]        = provinceFrontiersPoints [j];
                    provinceFrontiersIndices [meshIndex] [j - k] = j - k;
                }
            }

            // Create province borders container

            // Create province layer if needed
            if (provincesObj != null)
            {
                DestroyImmediate(provincesObj);
            }

            provincesObj           = new GameObject(PROVINCES_BORDERS_LAYER);
            provincesObj.hideFlags = HideFlags.DontSave;
            provincesObj.transform.SetParent(transform, false);
            provincesObj.transform.localPosition = Misc.Vector3zero;
            provincesObj.transform.localRotation = Quaternion.Euler(Misc.Vector3zero);
            provincesObj.layer = gameObject.layer;

            // Choose a borders mat
            if (_provincesColor.a < 1f)
            {
                provincesMatCurrent = provincesMatAlpha;
            }
            else
            {
                provincesMatCurrent = provincesMatOpaque;
            }
            provincesMatCurrent.color = _provincesColor;

            for (int k = 0; k < provinceFrontiers.Length; k++)
            {
                GameObject flayer = new GameObject("flayer");
                flayer.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
                flayer.transform.SetParent(provincesObj.transform, false);
                flayer.transform.localPosition = Misc.Vector3zero;
                flayer.transform.localRotation = Quaternion.Euler(Misc.Vector3zero);

                Mesh mesh = new Mesh();
                mesh.vertices = provinceFrontiers [k];
                mesh.SetIndices(provinceFrontiersIndices [k], MeshTopology.Lines, 0);
                mesh.RecalculateBounds();
                mesh.hideFlags = HideFlags.DontSave;

                MeshFilter mf = flayer.AddComponent <MeshFilter> ();
                mf.sharedMesh = mesh;

                MeshRenderer mr = flayer.AddComponent <MeshRenderer> ();
                mr.receiveShadows       = false;
                mr.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
                mr.shadowCastingMode    = UnityEngine.Rendering.ShadowCastingMode.Off;
                mr.sharedMaterial       = provincesMatCurrent;
            }

//			if (_showOutline) {
//				Country country = countries[countryIndex];
//				Region region = country.regions[country.mainRegionIndex];
//				provinceCountryOutlineRef = DrawCountryRegionOutline(region, provinceObj);
//			}
            return(true);
        }
        void ProvinceSubstractProvinceEnclaves(int provinceIndex, Region region, Poly2Tri.Polygon poly)
        {
            List <Region> negativeRegions = new List <Region>();

            for (int oc = 0; oc < _countries.Length; oc++)
            {
                Country ocCountry = _countries[oc];
                if (ocCountry.hidden || ocCountry.provinces == null)
                {
                    continue;
                }
                if (!ocCountry.regionsRect2D.Overlaps(region.latlonRect2D))
                {
                    continue;
                }
                for (int op = 0; op < ocCountry.provinces.Length; op++)
                {
                    Province opProvince = ocCountry.provinces[op];
                    if (opProvince == provinces[provinceIndex])
                    {
                        continue;
                    }
                    if (opProvince.regions == null)
                    {
                        continue;
                    }
                    if (opProvince.regionsRect2D.Overlaps(region.latlonRect2D, true))
                    {
                        Region oProvRegion = opProvince.regions[opProvince.mainRegionIndex];
                        if (region.Contains(oProvRegion))                               // just check main region of province for speed purposes
                        {
                            negativeRegions.Add(oProvRegion);
                        }
                    }
                }
            }
            // Collapse negative regions in big holes
            for (int nr = 0; nr < negativeRegions.Count - 1; nr++)
            {
                for (int nr2 = nr + 1; nr2 < negativeRegions.Count; nr2++)
                {
                    if (negativeRegions[nr].Intersects(negativeRegions[nr2]))
                    {
                        PolygonClipper pc = new PolygonClipper(negativeRegions[nr], negativeRegions[nr2]);
                        if (pc.Compute(PolygonOp.UNION, null))
                        {
                            negativeRegions.RemoveAt(nr2);
                            nr = -1;
                            break;
                        }
                    }
                }
            }

            // Substract holes
            for (int r = 0; r < negativeRegions.Count; r++)
            {
                Poly2Tri.Polygon polyHole = new Poly2Tri.Polygon(negativeRegions[r].latlon);
                poly.AddHole(polyHole);
            }
        }
        /// <summary>
        /// Used internally by the Map Editor. It will recalculate de boundaries and optimize frontiers based on new data of provinces array
        /// </summary>
        public void RefreshProvinceGeometry(int provinceIndex)
        {
            if (provinceIndex < 0 || provinceIndex >= provinces.Length)
            {
                return;
            }
            lastProvinceLookupCount = -1;
            float    maxVol   = 0;
            Province province = provinces [provinceIndex];

            if (province.regions == null)
            {
                ReadProvincePackedString(province);
            }
            int     regionCount = province.regions.Count;
            Vector2 minProvince = Misc.Vector2one * 1000;
            Vector2 maxProvince = -minProvince;

            for (int r = 0; r < regionCount; r++)
            {
                Region provinceRegion = province.regions [r];
                provinceRegion.entity      = province;                  // just in case one country has been deleted
                provinceRegion.regionIndex = r;                         // just in case a region has been deleted
                int     coorCount = provinceRegion.latlon.Length;
                Vector2 min       = Misc.Vector2one * 1000;
                Vector2 max       = -min;
                for (int c = 0; c < coorCount; c++)
                {
                    float x = provinceRegion.latlon [c].x;
                    float y = provinceRegion.latlon [c].y;
                    if (x < min.x)
                    {
                        min.x = x;
                    }
                    if (x > max.x)
                    {
                        max.x = x;
                    }
                    if (y < min.y)
                    {
                        min.y = y;
                    }
                    if (y > max.y)
                    {
                        max.y = y;
                    }
                }
                Vector3 normRegionCenter = (min + max) * 0.5f;
                provinceRegion.latlonCenter = normRegionCenter;

                if (min.x < minProvince.x)
                {
                    minProvince.x = min.x;
                }
                if (min.y < minProvince.y)
                {
                    minProvince.y = min.y;
                }
                if (max.x > maxProvince.x)
                {
                    maxProvince.x = max.x;
                }
                if (max.y > maxProvince.y)
                {
                    maxProvince.y = max.y;
                }
                provinceRegion.latlonRect2D = new Rect(min.x, min.y, Math.Abs(max.x - min.x), Mathf.Abs(max.y - min.y));
                provinceRegion.rect2DArea   = provinceRegion.latlonRect2D.width * provinceRegion.latlonRect2D.height;
                float vol = (max - min).sqrMagnitude;
                if (vol > maxVol)
                {
                    maxVol = vol;
                    province.mainRegionIndex = r;
                    province.latlonCenter    = provinceRegion.latlonCenter;
                }
            }
            province.regionsRect2D = new Rect(minProvince.x, minProvince.y, Math.Abs(maxProvince.x - minProvince.x), Mathf.Abs(maxProvince.y - minProvince.y));
        }
        public void ReadProvincePackedString(Province province)
        {
            string[] regions     = province.packedRegions.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
            int      regionCount = regions.Length;

            province.regions = new List <Region> (regionCount);
            float   maxVol      = float.MinValue;
            Vector2 minProvince = Misc.Vector2one * 1000;
            Vector2 maxProvince = -minProvince;

            for (int r = 0; r < regionCount; r++)
            {
                string[]  coordinates    = regions [r].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                int       coorCount      = coordinates.Length;
                Vector2   min            = Misc.Vector2one * 1000;
                Vector2   max            = -min;
                Region    provinceRegion = new Region(province, province.regions.Count);
                Vector2[] latlon         = new Vector2[coorCount];
                for (int c = 0; c < coorCount; c++)
                {
                    float lat, lon;
                    GetPointFromPackedString(coordinates[c], out lat, out lon);

//					if (province.name.Equals("Badghis") && r==0 && c==0) {
//					int x = (int)(lat * WorldMapGlobe.MAP_PRECISION);
//					Debug.Log (coordinates[c] + " " + x.ToString());
//					}

                    if (lat < min.x)
                    {
                        min.x = lat;
                    }
                    if (lat > max.x)
                    {
                        max.x = lat;
                    }
                    if (lon < min.y)
                    {
                        min.y = lon;
                    }
                    if (lon > max.y)
                    {
                        max.y = lon;
                    }
                    latlon[c] = new Vector2(lat, lon);
                }
                provinceRegion.latlon = latlon;
                Vector3 normRegionCenter = (min + max) * 0.5f;
                provinceRegion.latlonCenter = normRegionCenter;

                province.regions.Add(provinceRegion);

                // Calculate province bounding rect
                if (min.x < minProvince.x)
                {
                    minProvince.x = min.x;
                }
                if (min.y < minProvince.y)
                {
                    minProvince.y = min.y;
                }
                if (max.x > maxProvince.x)
                {
                    maxProvince.x = max.x;
                }
                if (max.y > maxProvince.y)
                {
                    maxProvince.y = max.y;
                }
                provinceRegion.latlonRect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
                provinceRegion.rect2DArea   = provinceRegion.latlonRect2D.width * provinceRegion.latlonRect2D.height;
                float vol = (max - min).sqrMagnitude;
                if (vol > maxVol)
                {
                    maxVol = vol;
                    province.mainRegionIndex = r;
                    province.latlonCenter    = provinceRegion.latlonCenter;
                }
            }
            province.regionsRect2D = new Rect(minProvince.x, minProvince.y, maxProvince.x - minProvince.x, maxProvince.y - minProvince.y);
        }
        public void SplitVertically()
        {
            if (entityIndex < 0 || entityIndex >= entities.Length)
            {
                return;
            }

            IAdminEntity   currentEntity = entities[entityIndex];
            Region         currentRegion = currentEntity.regions[regionIndex];
            List <Vector2> half1         = new List <Vector2>();
            List <Vector2> half2         = new List <Vector2>();
            int            prevSide      = 0;
            float          centerX       = currentRegion.latlonCenter.y; // (currentRegion.minMaxLon.x + currentRegion.minMaxLon.y)*0.5;

            for (int k = 0; k < currentRegion.latlon.Length; k++)
            {
                Vector2 p = currentRegion.latlon[k];
                if (p.y > centerX)                 // compare longitudes
                {
                    half1.Add(p);
                    if (prevSide == -1)
                    {
                        half2.Add(p);
                    }
                    prevSide = 1;
                }
                else
                {
                    half2.Add(p);
                    if (prevSide == 1)
                    {
                        half1.Add(p);
                    }
                    prevSide = -1;
                }
            }
            // Setup new entity
            IAdminEntity newEntity;

            if (currentEntity is Country)
            {
                string name = map.GetCountryUniqueName("New " + currentEntity.name);
                newEntity = new Country(name, ((Country)currentEntity).continent);
            }
            else
            {
                string name = map.GetProvinceUniqueName("New " + currentEntity.name);
                newEntity         = new Province(name, ((Province)currentEntity).countryIndex);
                newEntity.regions = new List <Region>();
            }

            // Update polygons
            Region newRegion = new Region(newEntity, 0);

            if (entities[countryIndex].latlonCenter.y > centerX)
            {
                currentRegion.latlon = half1.ToArray();
                newRegion.latlon     = half2.ToArray();
            }
            else
            {
                currentRegion.latlon = half2.ToArray();
                newRegion.latlon     = half1.ToArray();
            }
            map.RegionSanitize(currentRegion);
            SmoothRegion(currentRegion);
            map.RegionSanitize(newRegion);
            SmoothRegion(newRegion);

            newEntity.regions.Add(newRegion);
            int newEntityIndex = EntityAdd(newEntity);

            // Refresh old entity and selects the new
            if (currentEntity is Country)
            {
                map.RefreshCountryDefinition(newEntityIndex, null);
                map.RefreshCountryDefinition(countryIndex, null);
                SplitCities(countryIndex, newRegion);
                ClearSelection();
                RedrawFrontiers();
                countryIndex       = newEntityIndex;
                countryRegionIndex = 0;
                CountryRegionSelect();
                countryChanges = true;
                cityChanges    = true;
            }
            else
            {
                map.RefreshProvinceDefinition(newEntityIndex);
                map.RefreshProvinceDefinition(provinceIndex);
                highlightedRegions = null;
                ClearSelection();
                RedrawFrontiers();
                provinceIndex       = newEntityIndex;
                provinceRegionIndex = 0;
                countryIndex        = map.provinces[provinceIndex].countryIndex;
                countryRegionIndex  = map.countries[countryIndex].mainRegionIndex;
                CountryRegionSelect();
                ProvinceRegionSelect();
                provinceChanges = true;
            }
            map.RedrawMapLabels();
        }
        public bool GetVertexNearSpherePos(Vector3 spherePos, out Vector3 nearPoint)
        {
            // Iterate country regions
            int     numCountries = _map.countries.Length;
            Vector3 np           = spherePos;
            float   minDist      = float.MaxValue;

            // Countries
            for (int c = 0; c < numCountries; c++)
            {
                Country country  = _map.countries[c];
                int     regCount = country.regions.Count;
                for (int cr = 0; cr < regCount; cr++)
                {
                    Region region     = country.regions[cr];
                    int    pointCount = region.spherePoints.Length;
                    for (int p = 0; p < pointCount; p++)
                    {
                        float dist = (spherePos - region.spherePoints[p]).sqrMagnitude;
                        if (dist < minDist)
                        {
                            // Check that it's not already in the new shape
                            if (!newShape.Contains(region.spherePoints[p]))
                            {
                                minDist = dist;
                                np      = region.spherePoints[p];
                            }
                        }
                    }
                }
            }
            // Provinces
            if (_map.editor.editingMode == EDITING_MODE.PROVINCES)
            {
                int numProvinces = _map.provinces.Length;
                for (int p = 0; p < numProvinces; p++)
                {
                    Province province = _map.provinces[p];
                    if (province.regions == null)
                    {
                        continue;
                    }
                    int regCount = province.regions.Count;
                    for (int pr = 0; pr < regCount; pr++)
                    {
                        Region region     = province.regions[pr];
                        int    pointCount = region.spherePoints.Length;
                        for (int po = 0; po < pointCount; po++)
                        {
                            float dist = (spherePos - region.spherePoints[po]).sqrMagnitude;
                            if (dist < minDist)
                            {
                                // Check that it's not already in the new shape
                                if (!newShape.Contains(region.spherePoints[p]))
                                {
                                    minDist = dist;
                                    np      = region.spherePoints[p];
                                }
                            }
                        }
                    }
                }
            }

            nearPoint = np;
            return(nearPoint != spherePos);
        }
        /// <summary>
        /// Calculates correct province for cities
        /// </summary>
        public void FixOrphanCities()
        {
            if (_map.provinces == null)
            {
                return;
            }

            int countryAssigned  = 0;
            int provinceAssigned = 0;

            int cityCount = _map.cities.Count;

            for (int c = 0; c < cityCount; c++)
            {
                City city = _map.cities [c];
                if (city.countryIndex == -1)
                {
                    for (int k = 0; k < _map.countries.Length; k++)
                    {
                        Country co = _map.countries [k];
                        if (co.regions == null)
                        {
                            continue;
                        }
                        int regCount = co.regions.Count;
                        for (int kr = 0; kr < regCount; kr++)
                        {
                            if (co.regions [kr].Contains(city.latlon))
                            {
                                city.countryIndex = k;
                                countryAssigned++;
                                k = 100000;
                                break;
                            }
                        }
                    }
                }
                if (city.countryIndex == -1)
                {
                    float minDist = float.MaxValue;
                    for (int k = 0; k < _map.countries.Length; k++)
                    {
                        Country co = _map.countries [k];
                        if (co.regions == null)
                        {
                            continue;
                        }
                        int regCount = co.regions.Count;
                        for (int kr = 0; kr < regCount; kr++)
                        {
                            float dist = (co.regions [kr].latlonCenter - city.latlon).sqrMagnitude;
                            if (dist < minDist)
                            {
                                minDist           = dist;
                                city.countryIndex = k;
                                countryAssigned++;
                            }
                        }
                    }
                }

                if (city.province.Length == 0)
                {
                    Country country = _map.countries [city.countryIndex];
                    if (country.provinces == null)
                    {
                        continue;
                    }
                    for (int p = 0; p < country.provinces.Length; p++)
                    {
                        Province province = country.provinces [p];
                        if (province.regions == null)
                        {
                            _map.ReadProvincePackedString(province);
                        }
                        if (province.regions == null)
                        {
                            continue;
                        }
                        int regCount = province.regions.Count;
                        for (int pr = 0; pr < regCount; pr++)
                        {
                            Region reg = province.regions [pr];
                            if (reg.Contains(city.latlon))
                            {
                                city.province = province.name;
                                p             = 100000;
                                break;
                            }
                        }
                    }
                }
            }

            for (int c = 0; c < cityCount; c++)
            {
                City city = _map.cities [c];
                if (city.province.Length == 0)
                {
                    float minDist = float.MaxValue;
                    int   pg      = -1;
                    for (int p = 0; p < _map.provinces.Length; p++)
                    {
                        Province province = _map.provinces [p];
                        if (province.regions == null)
                        {
                            _map.ReadProvincePackedString(province);
                        }
                        if (province.regions == null)
                        {
                            continue;
                        }
                        int regCount = province.regions.Count;
                        for (int pr = 0; pr < regCount; pr++)
                        {
                            Region pregion = province.regions [pr];
                            for (int prp = 0; prp < pregion.latlon.Length; prp++)
                            {
                                float dist = (city.latlon - pregion.latlon [prp]).sqrMagnitude;
                                if (dist < minDist)
                                {
                                    minDist = dist;
                                    pg      = p;
                                }
                            }
                        }
                    }
                    if (pg >= 0)
                    {
                        city.province = _map.provinces [pg].name;
                        provinceAssigned++;
                    }
                }
                else                     // check for differences in capitals
                {
                                        #if !UNITY_WSA
                    if (!city.name.Equals(city.province) && String.Compare(city.name, city.province, System.Globalization.CultureInfo.CurrentCulture, System.Globalization.CompareOptions.IgnoreNonSpace) == 0)
                    {
                        city.name   = city.province;
                        cityChanges = true;
                    }
                                        #endif
                }
            }

            Debug.Log(countryAssigned + " cities were assigned a new country.");
            Debug.Log(provinceAssigned + " cities were assigned a new province.");

            if (countryAssigned > 0 || provinceAssigned > 0)
            {
                cityChanges = true;
            }
        }