How to Utilize the LineRenderer on Objects

763
1
04-11-2023 09:57 AM
TBraff
by
New Contributor II

Hello, I am having issues trying to use the Unity Line Renderer in version 2022.2.5f1 as well as placing objects on the map appropriately.

I have looked at the "Measuring Distance" sample as well as the "Routing" sample that look to utilize the Line Renderer system, but I can't seem to figure out exactly how theirs works.

Currently, I read a GeoJSON file and grab all the coordinates for each feature. I then create a gameObject to house the Line Renderer component, and then I go into the coordinates for the feature and create a gameObject for each coordinate point, adding an ArcGISLocationComponent to each. I then have a list of all these coordinate gameObjects, and then set the main LineRenderer positions to those gameObject positions, which is where I hit the snag.

I do not know if the LineRender component understands the difference between the High-Precision Transform and a regular Transform. I can confirm that the coordinate gameObjects are placed appropriately on the map, but I can't use their regular transform since it's all zeroed out, and the HPTransform gives me huge numbers that I don't think the LineRender understands as being HPTransform, as seen in the screenshot below.

Ideally, I would like to not have to create a gameObject for each individual coordinate/point, but I do not know how I can convert lat/long coordinates into ArcGIS map coordinates to Unity world coordinates let alone do this all via code.

EDIT: Additionally, sometimes seemingly at random the points that are set in the Line Renderer are all the same value, even though nothing has changed code wise. It's incredibly frustrating, as I don't know what's causing this or what values I should be inputting into the LineRenderer to properly do what I need it to do.

Is there a way I could create a scene layer from the GeoJSON file? From what I've seen it looks like I'd need to use ArcGISPro, but I'm unsure how I can export a scene layer from that to then use in Unity.

TBraff_0-1681231924905.png

Below is the base code that I've made. I've commented it out to explain my methodology. Is there anything in particular I am not doing?

 

using System.Collections;
using System.Collections.Generic;
using Esri.ArcGISMapsSDK.Components;
using Esri.ArcGISMapsSDK.Utils.GeoCoord;
using Esri.GameEngine.Geometry;
using Esri.HPFramework;
using GeoJSON;
using Newtonsoft.Json.Linq;
using UnityEngine;
using Unity.Mathematics;
using System.Drawing;

public class GeoJSONRenderer : MonoBehaviour
{
    // Start is called before the first frame update    
    public TextAsset jsonFile;
    public GameObject arcGISMap;
    public GameObject obj;
    public Material mat;

    private double3 lastRootPosition;

    ArcGISMapComponent arcGISMapComponent;

    List<LineRenderer> lineRenderers = new List<LineRenderer>();

    void Start()
    {
        arcGISMapComponent = FindObjectOfType<ArcGISMapComponent>();
        //lastRootPosition = arcGISMapComponent.GetComponent<HPRoot>().RootUniversePosition;

        // Delaying the LineRenderer creation so ArcGIS has time to generate the map
        StartCoroutine(TimingRenders());
    }

    private void RebaseLine(LineRenderer lr)
    {
        double3 rootPosition = arcGISMapComponent.GetComponent<HPRoot>().RootUniversePosition;
        Vector3 delta = (lastRootPosition - rootPosition).ToVector3();
        if (delta.magnitude > 1) // 1km
        {
            if (lr != null)
            {
                Vector3[] points = new Vector3[lr.positionCount];
                lr.GetPositions(points);
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] += delta;
                }
                lr.SetPositions(points);
            }
            lastRootPosition = rootPosition;
        }
    }

    private IEnumerator TimingRenders()
    {
        float Timer = 3.0f;
        yield return new WaitForSeconds(Timer);
        LineRendering();
    }

    // We run this function a few seconds after startup because for some reason ArcGIS needs a few seconds to get its **bleep** together
    private void LineRendering()
    {
        FeatureCollection col = GeoJSONObject.Deserialize(jsonFile.text);

        foreach (FeatureObject f in col.features)
        {
            // Get a list of all the position objects from the FeatureCollection JSON
            List<PositionObject> p = f.geometry.AllPositions();
            
            // Create a gameObject to house the LineRenderer for this particular collection
            GameObject lineObj = Instantiate(obj, transform.position, Quaternion.identity, arcGISMap.transform);
            LineRenderer lr = lineObj.AddComponent<LineRenderer>();
            ArcGISLocationComponent lineRenderLocComp = lineObj.AddComponent<ArcGISLocationComponent>();
            lineRenderLocComp.Position = new ArcGISPoint(p[0].longitude, p[0].latitude, 250.0f, ArcGISSpatialReference.WGS84());

            // Go through each position
            // Place them appropriately on the map using their coordinates
            List<GameObject> positions = new List<GameObject>();
            // ArcGISGeometry multiP = ArcGISGeometry.FromJSON(jsonFile.text, ArcGISSpatialReference.WGS84());
            
            foreach (PositionObject o in p)
            {
                // There's a 3000 Y offset set in for some reason, need to counter that
                ArcGISPoint point = new ArcGISPoint(o.longitude, o.latitude, 250.0f, ArcGISSpatialReference.WGS84());
                GameObject mainObj = Instantiate(obj, Vector3.zero, Quaternion.identity, arcGISMap.transform);

                ArcGISLocationComponent locComp = mainObj.AddComponent<ArcGISLocationComponent>();
                locComp.Position = point;
                positions.Add(mainObj);
                
                //locComp.SyncPositionWithHPTransform();
                //HPTransform hpTrans = mainObj.GetComponent<HPTransform>();

                // Vector3 worldPoint = arcGISMapComponent.View.GeographicToWorld(point).ToVector3();
                    //Add(new Vector3(
                    //(float)hpTrans.LocalPosition.x, 
                    //(float)hpTrans.LocalPosition.z, 
                    //(float)hpTrans.LocalPosition.y
                    //)
                //); // (worldPoint);
            }

            lr.positionCount = positions.Count;
            
            List<Vector3> points = new List<Vector3>();
            foreach(GameObject obj in positions)
            {
                points.Add(obj.transform.position);
            }

            lr.SetPositions(points.ToArray());
            
            lr.startWidth = 100.0f;
            lr.endWidth = 100.0f;

            lr.material = mat;
            lr.material.color = UnityEngine.Color.red;

            lineRenderers.Add(lr);
        }
    }

    // Update is called once per frame
    void Update()
    {
        foreach(LineRenderer lr in lineRenderers)
        {
            RebaseLine(lr);
        }
    }
}

 

 

 

0 Kudos
1 Reply
Jade
by Esri Contributor
Esri Contributor

Apologize for the late reply, so you can understand HP transform as a way to handle large transform values, it works similarly with regular transform. 

If you're still working on this, your general method is correct, although you don't need to create GO for each point and attach location component to each, you can directly convert from lat long to unity's world position by using 

var geoPosition = arcGISMapComponent.EngineToGeographic(hit.point);

Then use this value in linerenderer to render the line.

Let me know if that solves the problem, I haven't tried it out with code but happy to provide some code if you still need help.