# -----------------------------------------------------------------------------
# Python Script to use Geo-Data in C4D for maps 
# Also creates a network from the lacation points. 
# Andreas Pirchner 2020
# -----------------------------------------------------------------------------

import c4d
import json
import random
import math

# Needed C4D Objects:
# "CityPin", "MuseumPin", "SplineNet"

class museum:
    def __init__(self, name, lat, lon):
        self.name = name
        self.lat = lat
        self.lon = lon
        self.altitude = 0
        self.connections = 0

museumsList = []
connectionLimit = 1
scaling = 850
offsetLat = -47.8
offsetLong = -13.2
minDist = 0.1 * scaling
maxDist = 6 * scaling

def main():
    def tool():
        return c4d.plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)

    # Load JSON Data
    # -----------------------------------------------------------------------------
    with open ("./museumsDataAustria.json") as museumjson:   # load JSON for museums
        museumsData = json.load(museumjson)

    with open ("./citiesGPS.json") as cityjson:   # load JSON for cities
        citiesData = json.load(cityjson)

    # Make Point-Object-Array for Museums
    # -----------------------------------------------------------------------------
    for m in museumsData:
        #if m["Latitude"] > 42 and m["Latitude"] < 48 and m["Longitude"] > 11 and m["Longitude"] < 16:
        museumsList.append(museum(m["Institution"], (m["Latitude"]+offsetLat) *1.5 * scaling, (m["Longitude"]+offsetLong) * scaling))

    # Make Nulls as Containers for Museums and Cities
    # -----------------------------------------------------------------------------
    museumsContainer = c4d.BaseObject(c4d.Onull)            #make Null to contain all Museums
    museumsContainer.SetName("AllMuseums")
    doc.InsertObject(museumsContainer)
    citiesContainer = c4d.BaseObject(c4d.Onull)            #make Null to contain all Cities
    citiesContainer.SetName("AllCities")
    doc.InsertObject(citiesContainer)
    c4d.EventAdd()

    # Make Spline of all GPS Points
    # -----------------------------------------------------------------------------

    # 1 Make Spline of all GPS Points
    projectionSpline = c4d.SplineObject(len(museumsList), c4d.SPLINETYPE_LINEAR)
    t=0
    for p in museumsList:
        projectionSpline.SetPoint(t, c4d.Vector(p.lon, p.altitude, p.lat)) # altitude is increased to make sure that spline sits above terrain
        t = t+1
    projectionSpline.SetName("tempSpline")
    doc.InsertObject(projectionSpline)

    # 2 Project Spline to underlying Topography
    tmpSplineObject = doc.SearchObject("tempSpline")
    tmpSplineObject.SetBit(c4d.BIT_ACTIVE)
    c4d.CallCommand(450000046)                        # C4D Command: Spline/Move/Project
    tool()[c4d.MDATA_SPLINE_PROJECT_MODE] = 3         # Select Mode: XZ-Plane in Projection-Tool
    c4d.CallButton(tool(), c4d.MDATA_APPLY)           # Press Apply-Button in Tool

    # 3 Upate Point-Object-Array with Y-Values of Spline-Points
    ps = tmpSplineObject.GetAllPoints()
    print len(ps)
    i = 0
    for mu in museumsList:                            #
        mu.altitude = 0 # ps[i][1]                            # set altitude of museums point to Y of spline point
        i = i +1

    # 4 Delete Spline


    # Make City Pins
    # -----------------------------------------------------------------------------
    for c in citiesData:
        cityX = (c["long"]+offsetLong) * scaling
        cityZ = (c["lat"]+offsetLat)* scaling * 1.5
        cityY = 0

        #cityY = c["altitude"]/100
        cityName = c["City"]
        originalCity = doc.SearchObject("CityPin")
        city = originalCity.GetClone()
        city.SetAbsPos(c4d.Vector(cityX,cityY,cityZ))    # set the position
        #city.SetAbsScale(c4d.Vector(0.3,0.3,0.3))
        doc.InsertObject(city)
        city.InsertUnder(citiesContainer)               # add it in the Null c4d.EventAdd()

    # Make Network Connections
    # -----------------------------------------------------------------------------
    splineContainer = doc.SearchObject("SplineNet")

    for mus in museumsList:                                                           # loop through all museums
        p1 = c4d.Vector(mus.lon, mus.altitude, mus.lat)                               # get first point

        if mus.connections < connectionLimit:                                         # check the connection limit for the first point
            for k in museumsList:                                                     # loop through all points
                if k.connections < connectionLimit and mus.connections < connectionLimit:    # check limit for second point

                    p3 = c4d.Vector(k.lon, mus.altitude,k.lat)                        # get second point
                    dist = c4d.Vector.GetDistance(p1,p3)                              # get distance of the 2 points

                    if dist < maxDist and dist > minDist:                             # check if distance is withon limits
                        mP = p1+(p3-p1)/2                                             # calculate middle vector between 2 vectors
                        mP[1] = mP[1]+dist/1.5                                         # set height (y) of the arc as a fourth of the distance
                        #print(p1)
                        #print(p3)
                        spline = c4d.SplineObject(3, c4d.SPLINETYPE_CUBIC)            # make the spline of the 3 points
                        spline.SetPoint(0, p1)
                        spline.SetPoint(1, mP)
                        spline.SetPoint(2, p3)
                        spline.InsertUnder(splineContainer)
                        k.connections = k.connections + 1                            #update connections number for point 2
                        mus.connections =  mus.connections + 1                       # ... and for point 1
                        print(dist)

        # Make Museums Pins
        # -----------------------------------------------------------------------------
        original = doc.SearchObject("MuseumPin")
        obj = original.GetClone()
        obj.SetAbsPos(p1)    # set the position
        obj.SetAbsScale(c4d.Vector(70,70,70))
        rotation = random.randint(0,359);
        obj.SetAbsRot(c4d.Vector(rotation,0,0))
        doc.InsertObject(obj)
        obj.InsertUnder(museumsContainer)               # add it in the Null c4d.EventAdd()

    c4d.EventAdd()
    print("done")

# Execute main()
if __name__=='__main__':
    main()